mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 19:45:09 +00:00
ewsdrd.lpr — a console daemon that runs TRadioController + TWebAdapter WITHOUT
TMainForm/LCL, proving the Phase-5 goal: web-only headless operation. THeadlessHost
wires the data-path callbacks to the controller, the web command handlers to the
adapter (lifecycle ones marshalled to the main thread via FCtrl.Invoke =
TThread.Synchronize), opens WDSP synchronously, autostarts the saved device, and
runs a CheckSynchronize + periodic PushState loop. SIGINT/SIGTERM → graceful
StopAndDisconnect (persist + Run=0).
To keep the controller graph truly LCL-free, PlatformUtils now guards its only
Forms dependency (Screen.PixelsPerInch in CurrentScreenDPI) behind {$IFNDEF
HEADLESS}; the GUI build is byte-identical (HEADLESS undefined). build-ewsdrd.sh
compiles the headless graph from source with -Mobjfpc (nested comments) -dHEADLESS
into a separate lib-headless/ output dir, untouching the GUI .ppu.
Verified: builds clean; runs headless, web server up (HTTP 401 = serving), no LCL;
SIGINT shuts down gracefully. Radio-connect + web round-trip need hardware to test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
90 lines
2.1 KiB
ObjectPascal
90 lines
2.1 KiB
ObjectPascal
unit PlatformUtils;
|
|
|
|
{
|
|
PlatformUtils.pas - small helpers for desktop/runtime platform checks.
|
|
}
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE Delphi}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
function QtPlatformAllowsOpenGLControls: Boolean;
|
|
function HasOpenGLSpectrumSwitch: Boolean;
|
|
function CurrentScreenDPI: Integer;
|
|
// Возвращает каталог для хранения конфигурации (с завершающим разделителем).
|
|
// macOS: ~/Library/Application Support/ewsdr/
|
|
// Linux: ~/.config/ewsdr/
|
|
// Windows: %APPDATA%\ewsdr\
|
|
function GetAppCfgDir: string;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils {$IFNDEF HEADLESS}, Forms{$ENDIF};
|
|
|
|
function QtPlatformAllowsOpenGLControls: Boolean;
|
|
var
|
|
Qpa, FirstQpa, SessionType: string;
|
|
Sep: Integer;
|
|
begin
|
|
Result := True;
|
|
Qpa := LowerCase(Trim(GetEnvironmentVariable('QT_QPA_PLATFORM')));
|
|
SessionType := LowerCase(Trim(GetEnvironmentVariable('XDG_SESSION_TYPE')));
|
|
|
|
if Qpa <> '' then
|
|
begin
|
|
FirstQpa := Qpa;
|
|
Sep := Pos(';', FirstQpa);
|
|
if Sep > 0 then
|
|
FirstQpa := Copy(FirstQpa, 1, Sep - 1);
|
|
Result := FirstQpa <> 'wayland';
|
|
Exit;
|
|
end;
|
|
|
|
Result := SessionType <> 'wayland';
|
|
end;
|
|
|
|
function HasOpenGLSpectrumSwitch: Boolean;
|
|
var
|
|
I: Integer;
|
|
S: string;
|
|
begin
|
|
Result := False;
|
|
for I := 1 to ParamCount do
|
|
begin
|
|
S := LowerCase(ParamStr(I));
|
|
if (S = '--opengl') or (S = '-opengl') or (S = '/opengl') then
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
|
|
function CurrentScreenDPI: Integer;
|
|
begin
|
|
{$IFNDEF HEADLESS}
|
|
Result := Screen.PixelsPerInch;
|
|
if Result <= 0 then Result := 96;
|
|
{$ELSE}
|
|
Result := 96; // headless-демон без LCL: экрана нет, дефолт DPI
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function GetAppCfgDir: string;
|
|
begin
|
|
{$IFDEF WINDOWS}
|
|
Result := GetEnvironmentVariable('APPDATA');
|
|
if Result = '' then
|
|
Result := GetEnvironmentVariable('LOCALAPPDATA');
|
|
if Result = '' then
|
|
Result := ExtractFilePath(ParamStr(0));
|
|
Result := IncludeTrailingPathDelimiter(Result) + 'ewsdr' + PathDelim;
|
|
{$ELSE}
|
|
Result := IncludeTrailingPathDelimiter(GetAppConfigDir(False));
|
|
{$ENDIF}
|
|
if not DirectoryExists(Result) then
|
|
ForceDirectories(Result);
|
|
end;
|
|
|
|
end.
|