Prevent OS sleep and display blanking while SDR is running

Add PowerInhibit.pas with InhibitSleep/UninhibitSleep:
- Windows: SetThreadExecutionState (kernel32, no child process)
- macOS: caffeinate -d -i -s -w <PID> (auto-exits on crash via -w flag)
- Linux: systemd-inhibit with kill-0 poll loop (releases within 5s on crash)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-31 21:33:30 +03:00
co-authored by Claude Sonnet 4.6
parent 655d242d02
commit 83ec28f997
3 changed files with 120 additions and 1 deletions
+7 -1
View File
@@ -39,7 +39,8 @@ uses
WinFirewall, WinFirewall,
BoardUtils, WisdomBuilder, UISync, BoardUtils, WisdomBuilder, UISync,
FlatEdit, FMRepeater, FlatEdit, FMRepeater,
ChannelStore, ChannelsForm; ChannelStore, ChannelsForm,
PowerInhibit;
const const
CLR_BG = TColor($00101010); CLR_BG = TColor($00101010);
@@ -1464,6 +1465,7 @@ begin
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), 0, False, True, True); FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), 0, False, True, True);
FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0); FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0);
FRunning := False; FRunning := False;
UninhibitSleep;
FTransmitting := False; FTransmitting := False;
if FWDSPReady then FDSPEngine.SetTXRun(False); if FWDSPReady then FDSPEngine.SetTXRun(False);
end; end;
@@ -3046,6 +3048,7 @@ begin
BtnStartStop.Caption := 'START'; BtnStartStop.Caption := 'START';
StyleButton(BtnStartStop, False); StyleButton(BtnStartStop, False);
FRunning := False; FRunning := False;
UninhibitSleep;
FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0); FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0);
SetRadioOfflineStatus('Hardware timeout'); SetRadioOfflineStatus('Hardware timeout');
BtnMOX.Enabled := False; BtnMOX.Enabled := False;
@@ -3057,6 +3060,7 @@ begin
BtnStartStop.Caption := 'START'; BtnStartStop.Caption := 'START';
StyleButton(BtnStartStop, False); StyleButton(BtnStartStop, False);
FRunning := False; FRunning := False;
UninhibitSleep;
FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0); FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0);
SetRadioOfflineStatus('Connection lost'); SetRadioOfflineStatus('Connection lost');
BtnMOX.Enabled := False; BtnMOX.Enabled := False;
@@ -3897,6 +3901,7 @@ begin
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), 0, False, True, True); FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), 0, False, True, True);
FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0); FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0);
FRunning := False; FRunning := False;
UninhibitSleep;
FTransmitting := False; FTransmitting := False;
if FWDSPReady then FDSPEngine.SetTXRun(False); if FWDSPReady then FDSPEngine.SetTXRun(False);
StyleButton(BtnMOX, False); StyleButton(BtnMOX, False);
@@ -4220,6 +4225,7 @@ begin
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FDriveLevel, False, True, True); FNetwork.UpdateState(XvtrTranslate(FCenterFreq), XvtrTranslate(ActiveTXFreqHz), FDriveLevel, False, True, True);
FNetwork.SetRunAndFreq(True, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), FDriveLevel); FNetwork.SetRunAndFreq(True, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), FDriveLevel);
FRunning := True; FRunning := True;
InhibitSleep;
FRXStartTime := GetTickCount64; FRXStartTime := GetTickCount64;
FRXLastPktTime := 0; FRXLastPktTime := 0;
FRXPacketCount := 0; FRXPacketCount := 0;
+109
View File
@@ -0,0 +1,109 @@
unit PowerInhibit;
{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
{
Prevents OS idle sleep and display blanking while the SDR is running.
Windows : SetThreadExecutionState (kernel32) — one call, no child process.
macOS : spawns /usr/bin/caffeinate -d -i -s -w <OurPID>
caffeinate exits automatically when our process exits (crash-safe).
Linux : spawns systemd-inhibit in --mode=block with a shell loop that
polls kill -0 <OurPID> so the inhibitor is released within
~5 s even on a crash (no orphan leak).
}
interface
procedure InhibitSleep;
procedure UninhibitSleep;
implementation
uses
SysUtils, Process
{$IFDEF WINDOWS}, Windows{$ENDIF};
{$IFDEF WINDOWS}
type
EXECUTION_STATE = DWORD;
const
ES_CONTINUOUS = DWORD($80000000);
ES_SYSTEM_REQUIRED = DWORD($00000001);
ES_DISPLAY_REQUIRED = DWORD($00000002);
function SetThreadExecutionState(esFlags: EXECUTION_STATE): EXECUTION_STATE; stdcall;
external 'kernel32.dll' name 'SetThreadExecutionState';
{$ENDIF}
var
FInhibitProc: TProcess = nil;
procedure InhibitSleep;
{$IF DEFINED(DARWIN) OR DEFINED(LINUX)}
var
PidStr: string;
{$ENDIF}
begin
{$IFDEF WINDOWS}
SetThreadExecutionState(ES_CONTINUOUS or ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED);
{$ENDIF}
{$IF DEFINED(DARWIN) OR DEFINED(LINUX)}
if FInhibitProc <> nil then Exit;
PidStr := IntToStr(GetProcessID);
FInhibitProc := TProcess.Create(nil);
try
{$IFDEF DARWIN}
FInhibitProc.Executable := '/usr/bin/caffeinate';
FInhibitProc.Parameters.Add('-d'); // prevent display sleep
FInhibitProc.Parameters.Add('-i'); // prevent idle sleep
FInhibitProc.Parameters.Add('-s'); // prevent system sleep (AC power)
FInhibitProc.Parameters.Add('-w'); // watch our PID — caffeinate auto-exits with us
FInhibitProc.Parameters.Add(PidStr);
{$ENDIF}
{$IFDEF LINUX}
// systemd-inhibit holds the lock until the sh loop exits.
// The loop polls kill -0 <PID> every 5 s, so if we crash the
// inhibitor is released within 5 s (no orphan process leak).
FInhibitProc.Executable := 'systemd-inhibit';
FInhibitProc.Parameters.Add('--what=idle:sleep:handle-lid-switch');
FInhibitProc.Parameters.Add('--why=SDR running');
FInhibitProc.Parameters.Add('--who=ewsdr');
FInhibitProc.Parameters.Add('--mode=block');
FInhibitProc.Parameters.Add('sh');
FInhibitProc.Parameters.Add('-c');
FInhibitProc.Parameters.Add('while kill -0 ' + PidStr + ' 2>/dev/null; do sleep 5; done');
{$ENDIF}
FInhibitProc.Options := [poNoConsole];
FInhibitProc.Execute;
except
FreeAndNil(FInhibitProc);
end;
{$ENDIF}
end;
procedure UninhibitSleep;
begin
{$IFDEF WINDOWS}
SetThreadExecutionState(ES_CONTINUOUS);
{$ENDIF}
{$IF DEFINED(DARWIN) OR DEFINED(LINUX)}
if FInhibitProc <> nil then
begin
if FInhibitProc.Running then
FInhibitProc.Terminate(0);
FreeAndNil(FInhibitProc);
end;
{$ENDIF}
end;
initialization
FInhibitProc := nil;
finalization
UninhibitSleep;
end.
+4
View File
@@ -229,6 +229,10 @@
<Filename Value="PlatformUtils.pas"/> <Filename Value="PlatformUtils.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
</Unit> </Unit>
<Unit>
<Filename Value="PowerInhibit.pas"/>
<IsPartOfProject Value="True"/>
</Unit>
<Unit> <Unit>
<Filename Value="BoardUtils.pas"/> <Filename Value="BoardUtils.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>