From 3607f1d76825b2abb8a0b0dd6ffc2146f5cd1997 Mon Sep 17 00:00:00 2001 From: yuanyuanxiang <962914132@qq.com> Date: Fri, 1 May 2026 21:43:55 +0200 Subject: [PATCH] Feature: Add power management to keep macOS client always responsive --- macos/ScreenHandler.h | 4 ++++ macos/ScreenHandler.mm | 23 +++++++++++++++++++++++ macos/main.mm | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/macos/ScreenHandler.h b/macos/ScreenHandler.h index 286f3ea..6118915 100644 --- a/macos/ScreenHandler.h +++ b/macos/ScreenHandler.h @@ -2,6 +2,7 @@ #import #import +#import #import "../client/IOCPClient.h" #include #include @@ -132,4 +133,7 @@ private: // Input handler for mouse/keyboard control std::unique_ptr m_inputHandler; + + // Power management: prevent display sleep during remote desktop + IOPMAssertionID m_displayAssertionID; }; diff --git a/macos/ScreenHandler.mm b/macos/ScreenHandler.mm index 2ac622e..b7075e6 100644 --- a/macos/ScreenHandler.mm +++ b/macos/ScreenHandler.mm @@ -26,6 +26,7 @@ ScreenHandler::ScreenHandler(IOCPClient* client) , m_maxFPS(15) , m_qualityLevel(QUALITY_GOOD) // Use fixed QUALITY_GOOD (H264) for web compatibility , m_h264Bitrate(3000000) // 3 Mbps (matches Windows QUALITY_GOOD) + , m_displayAssertionID(0) { memset(&m_bmpHeader, 0, sizeof(m_bmpHeader)); @@ -115,6 +116,21 @@ void ScreenHandler::start(IOCPClient* client, uint64_t clientID) m_clientID = clientID; m_running = true; + // Prevent display sleep during remote desktop session + if (m_displayAssertionID == 0) { + IOReturn result = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, + CFSTR("SimpleRemoter - remote desktop session active"), + &m_displayAssertionID + ); + if (result == kIOReturnSuccess) { + NSLog(@"Display sleep disabled for remote desktop (ID: %u)", m_displayAssertionID); + } else { + NSLog(@"Warning: Failed to prevent display sleep (error: 0x%x)", result); + } + } + m_captureThread = std::thread(&ScreenHandler::captureLoop, this); } @@ -130,6 +146,13 @@ void ScreenHandler::stop() m_h264Encoder->close(); m_h264Encoder.reset(); } + + // Release display sleep assertion - allow screen to turn off + if (m_displayAssertionID != 0) { + IOPMAssertionRelease(m_displayAssertionID); + NSLog(@"Display sleep re-enabled (released ID: %u)", m_displayAssertionID); + m_displayAssertionID = 0; + } } void ScreenHandler::sendBitmapInfo() diff --git a/macos/main.mm b/macos/main.mm index 81f673e..c928dc6 100644 --- a/macos/main.mm +++ b/macos/main.mm @@ -6,6 +6,7 @@ #import #import #import +#import #import #import #import @@ -478,6 +479,30 @@ int main(int argc, const char* argv[]) @autoreleasepool { NSLog(@"=== macOS Ghost Client ==="); + // ============== Power Management: Keep System Awake ============== + // 1. Disable App Nap - prevent macOS from suspending this process + id powerActivity = [[NSProcessInfo processInfo] + beginActivityWithOptions:(NSActivityUserInitiated | NSActivityIdleSystemSleepDisabled) + reason:@"Remote control client must maintain persistent connection"]; + NSLog(@"App Nap disabled, activity token acquired"); + + // 2. Prevent system idle sleep using IOKit power assertion + IOPMAssertionID sleepAssertionID = 0; + IOReturn result = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoIdleSleep, + kIOPMAssertionLevelOn, + CFSTR("SimpleRemoter macOS client - maintaining remote connection"), + &sleepAssertionID + ); + if (result == kIOReturnSuccess) { + NSLog(@"Power assertion created: system idle sleep disabled (ID: %u)", sleepAssertionID); + } else { + NSLog(@"Warning: Failed to create power assertion (error: 0x%x)", result); + } + + // 3. Display sleep: managed by ScreenHandler - only prevents display sleep + // when remote desktop is actively connected (saves power when idle) + // Setup signal handlers setupSignals(); @@ -549,6 +574,15 @@ int main(int argc, const char* argv[]) } NSLog(@"Shutting down..."); + + // Release power assertions + if (sleepAssertionID) { + IOPMAssertionRelease(sleepAssertionID); + NSLog(@"Released sleep assertion"); + } + // Display assertion is managed by ScreenHandler (released in stop()) + // powerActivity is automatically released when exiting @autoreleasepool + (void)powerActivity; // Suppress unused variable warning } return 0;