feat: desktop mode falls back to the TUI when no graphical display is available (issue #33)
This commit is contained in:
@@ -57,7 +57,7 @@ defmodule BDS.Application do
|
|||||||
{Task.Supervisor, name: BDS.Scripting.TaskSupervisor},
|
{Task.Supervisor, name: BDS.Scripting.TaskSupervisor},
|
||||||
BDS.Scripting.JobSupervisor,
|
BDS.Scripting.JobSupervisor,
|
||||||
BDS.Embeddings.Index
|
BDS.Embeddings.Index
|
||||||
] ++ embedding_children() ++ mode_children(BDS.Server.mode(), current_env())
|
] ++ embedding_children() ++ mode_children(boot_mode(), current_env())
|
||||||
|
|
||||||
opts = [strategy: :one_for_one, name: BDS.Supervisor]
|
opts = [strategy: :one_for_one, name: BDS.Supervisor]
|
||||||
Supervisor.start_link(children, opts)
|
Supervisor.start_link(children, opts)
|
||||||
@@ -77,6 +77,20 @@ defmodule BDS.Application do
|
|||||||
Application.get_env(:bds, :current_env_override) || @compiled_env
|
Application.get_env(:bds, :current_env_override) || @compiled_env
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Desktop mode without a graphical display boots the TUI instead
|
||||||
|
# (issue #33); the log line explains the surprise mode switch.
|
||||||
|
defp boot_mode do
|
||||||
|
resolved = BDS.Server.mode()
|
||||||
|
effective = BDS.Server.effective_mode(resolved)
|
||||||
|
|
||||||
|
if effective != resolved do
|
||||||
|
require Logger
|
||||||
|
Logger.info("No graphical display available, starting the terminal UI instead")
|
||||||
|
end
|
||||||
|
|
||||||
|
effective
|
||||||
|
end
|
||||||
|
|
||||||
defp desktop_window_children do
|
defp desktop_window_children do
|
||||||
if desktop_automation?() do
|
if desktop_automation?() do
|
||||||
[]
|
[]
|
||||||
|
|||||||
@@ -28,6 +28,29 @@ defmodule BDS.Server do
|
|||||||
|
|
||||||
def mode(nil), do: :desktop
|
def mode(nil), do: :desktop
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
The mode the app actually boots in: the resolved `BDS_MODE`, except that
|
||||||
|
desktop mode falls back to the TUI when no graphical display is available
|
||||||
|
(issue #33). Starting the app on a display-less Linux server then still
|
||||||
|
opens a UI — the terminal one — instead of crashing wx.
|
||||||
|
|
||||||
|
Only non-Darwin unix systems fall back: they need `DISPLAY` or
|
||||||
|
`WAYLAND_DISPLAY` to run wx, while macOS and Windows always have a
|
||||||
|
graphical session for a launched app.
|
||||||
|
"""
|
||||||
|
@spec effective_mode(:desktop | :server | :tui, {atom(), atom()}, %{
|
||||||
|
optional(String.t()) => String.t()
|
||||||
|
}) :: :desktop | :server | :tui
|
||||||
|
def effective_mode(mode \\ mode(), os_type \\ :os.type(), env \\ System.get_env())
|
||||||
|
|
||||||
|
def effective_mode(:desktop, {:unix, os}, env) when os != :darwin do
|
||||||
|
if Enum.any?(["DISPLAY", "WAYLAND_DISPLAY"], &(env[&1] not in [nil, ""])),
|
||||||
|
do: :desktop,
|
||||||
|
else: :tui
|
||||||
|
end
|
||||||
|
|
||||||
|
def effective_mode(mode, _os_type, _env), do: mode
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Whether the HTTP endpoint requires the desktop webview auth token. Only
|
Whether the HTTP endpoint requires the desktop webview auth token. Only
|
||||||
desktop mode does: in server/tui mode the endpoint stays loopback-only
|
desktop mode does: in server/tui mode the endpoint stays loopback-only
|
||||||
@@ -35,7 +58,7 @@ defmodule BDS.Server do
|
|||||||
per-boot webview token would otherwise lock out.
|
per-boot webview token would otherwise lock out.
|
||||||
"""
|
"""
|
||||||
@spec desktop_auth_required?(:desktop | :server | :tui) :: boolean()
|
@spec desktop_auth_required?(:desktop | :server | :tui) :: boolean()
|
||||||
def desktop_auth_required?(mode \\ mode())
|
def desktop_auth_required?(mode \\ effective_mode())
|
||||||
def desktop_auth_required?(:desktop), do: true
|
def desktop_auth_required?(:desktop), do: true
|
||||||
def desktop_auth_required?(_mode), do: false
|
def desktop_auth_required?(_mode), do: false
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,12 @@ surface ServerRuntimeSurface {
|
|||||||
|
|
||||||
rule ModeResolution {
|
rule ModeResolution {
|
||||||
when: ApplicationStarted(mode)
|
when: ApplicationStarted(mode)
|
||||||
-- BDS_MODE environment variable decides the mode once per boot
|
-- BDS_MODE environment variable decides the mode once per boot.
|
||||||
|
-- Fallback (issue #33): resolved desktop mode on a non-Darwin unix
|
||||||
|
-- system without DISPLAY/WAYLAND_DISPLAY boots as tui instead, so a
|
||||||
|
-- display-less Linux server still opens a UI rather than crashing wx;
|
||||||
|
-- a log line records the switch. Explicit server/tui modes and
|
||||||
|
-- macOS/Windows are never rewritten.
|
||||||
ensures: BootMode.created(kind: mode)
|
ensures: BootMode.created(kind: mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,32 @@ defmodule BDS.ServerTest do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "effective_mode/3" do
|
||||||
|
test "desktop stays desktop when a display is available" do
|
||||||
|
assert Server.effective_mode(:desktop, {:unix, :linux}, %{"DISPLAY" => ":0"}) == :desktop
|
||||||
|
|
||||||
|
assert Server.effective_mode(:desktop, {:unix, :linux}, %{"WAYLAND_DISPLAY" => "wayland-0"}) ==
|
||||||
|
:desktop
|
||||||
|
end
|
||||||
|
|
||||||
|
test "desktop falls back to the TUI on unix without a display" do
|
||||||
|
assert Server.effective_mode(:desktop, {:unix, :linux}, %{}) == :tui
|
||||||
|
assert Server.effective_mode(:desktop, {:unix, :linux}, %{"DISPLAY" => ""}) == :tui
|
||||||
|
assert Server.effective_mode(:desktop, {:unix, :freebsd}, %{"WAYLAND_DISPLAY" => ""}) == :tui
|
||||||
|
end
|
||||||
|
|
||||||
|
test "macOS and Windows never fall back" do
|
||||||
|
assert Server.effective_mode(:desktop, {:unix, :darwin}, %{}) == :desktop
|
||||||
|
assert Server.effective_mode(:desktop, {:win32, :nt}, %{}) == :desktop
|
||||||
|
end
|
||||||
|
|
||||||
|
test "explicit server and tui modes are untouched" do
|
||||||
|
assert Server.effective_mode(:server, {:unix, :linux}, %{}) == :server
|
||||||
|
assert Server.effective_mode(:tui, {:unix, :linux}, %{"DISPLAY" => ":0"}) == :tui
|
||||||
|
assert Server.effective_mode(:server, {:unix, :darwin}, %{"DISPLAY" => ":0"}) == :server
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "ensure_ssh_dir!/1" do
|
describe "ensure_ssh_dir!/1" do
|
||||||
setup do
|
setup do
|
||||||
dir = Path.join(System.tmp_dir!(), "bds-ssh-test-#{System.unique_integer([:positive])}")
|
dir = Path.join(System.tmp_dir!(), "bds-ssh-test-#{System.unique_integer([:positive])}")
|
||||||
|
|||||||
Reference in New Issue
Block a user