{ Copyright (C) 2026 - Uladzimir Karpenka, EW8BAK This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. } 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 caffeinate exits automatically when our process exits (crash-safe). Linux : spawns systemd-inhibit in --mode=block with a shell loop that polls kill -0 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 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.