Files
SubMiner/plugin/subminer/environment.lua

267 lines
7.2 KiB
Lua

local M = {}
local unpack_fn = table.unpack or unpack
function M.create(ctx)
local mp = ctx.mp
local utils = ctx.utils
local detected_backend = nil
local app_running_cache_value = nil
local app_running_cache_time = nil
local app_running_check_inflight = false
local app_running_waiters = {}
local APP_RUNNING_CACHE_TTL_SECONDS = 2
local function is_windows()
return package.config:sub(1, 1) == "\\"
end
local function is_macos()
local platform = mp.get_property("platform") or ""
if platform == "macos" or platform == "darwin" then
return true
end
local ostype = os.getenv("OSTYPE") or ""
return ostype:find("darwin") ~= nil
end
local function default_socket_path()
if is_windows() then
return "\\\\.\\pipe\\subminer-socket"
end
return "/tmp/subminer-socket"
end
local function path_separator()
return is_windows() and "\\" or "/"
end
local function join_path(...)
local parts = { ... }
if utils and type(utils.join_path) == "function" then
return utils.join_path(unpack_fn(parts))
end
return table.concat(parts, path_separator())
end
local function file_exists(path)
if not utils or type(utils.file_info) ~= "function" then
return false
end
return utils.file_info(path) ~= nil
end
local function resolve_subminer_config_dir()
local home = os.getenv("HOME") or os.getenv("USERPROFILE") or ""
local candidates = {}
if is_windows() then
local app_data = os.getenv("APPDATA") or join_path(home, "AppData", "Roaming")
candidates = {
join_path(app_data, "SubMiner"),
}
else
local xdg_config_home = os.getenv("XDG_CONFIG_HOME")
local primary_base = (type(xdg_config_home) == "string" and xdg_config_home ~= "")
and xdg_config_home
or join_path(home, ".config")
candidates = {
join_path(primary_base, "SubMiner"),
join_path(home, ".config", "SubMiner"),
}
end
for _, dir in ipairs(candidates) do
if file_exists(join_path(dir, "config.jsonc")) or file_exists(join_path(dir, "config.json")) or file_exists(dir) then
return dir
end
end
return candidates[1]
end
local function resolve_session_bindings_artifact_path()
return join_path(resolve_subminer_config_dir(), "session-bindings.json")
end
local function is_linux()
return not is_windows() and not is_macos()
end
local function now_seconds()
if type(mp.get_time) == "function" then
local value = tonumber(mp.get_time())
if value then
return value
end
end
return os.time()
end
local function process_list_has_subminer(raw_process_list)
if type(raw_process_list) ~= "string" then
return false
end
local process_list = raw_process_list:lower()
for line in process_list:gmatch("[^\n]+") do
if is_windows() then
local image = line:match('^"([^"]+)","')
if not image then
image = line:match('^"([^"]+)"')
end
if not image then
goto continue
end
if image == "subminer" or image == "subminer.exe" or image == "subminer.appimage" or image == "subminer.app" then
return true
end
if image:find("subminer", 1, true) and not image:find(".lua", 1, true) then
return true
end
else
local argv0 = line:match('^"([^"]+)"') or line:match("^%s*([^%s]+)")
if not argv0 then
goto continue
end
if argv0:find("subminer.lua", 1, true) or argv0:find("subminer.conf", 1, true) then
goto continue
end
local exe = argv0:match("([^/\\]+)$") or argv0
if exe == "SubMiner" or exe == "SubMiner.AppImage" or exe == "SubMiner.exe" or exe == "subminer" or exe == "subminer.appimage" or exe == "subminer.exe" then
return true
end
if exe:find("subminer", 1, true) and exe:find("%.lua", 1, true) == nil and exe:find("%.app", 1, true) == nil then
return true
end
end
::continue::
end
return false
end
local function process_scan_command()
if is_windows() then
return { "tasklist", "/FO", "CSV", "/NH" }
end
return { "ps", "-A", "-o", "args=" }
end
local function is_subminer_process_running()
local result = mp.command_native({
name = "subprocess",
args = process_scan_command(),
playback_only = false,
capture_stdout = true,
capture_stderr = false,
})
if not result or result.status ~= 0 then
return false
end
return process_list_has_subminer(result.stdout)
end
local function flush_app_running_waiters(value)
local waiters = app_running_waiters
app_running_waiters = {}
for _, waiter in ipairs(waiters) do
waiter(value)
end
end
local function is_subminer_app_running_async(callback, opts)
opts = opts or {}
local force_refresh = opts.force_refresh == true
local now = now_seconds()
if not force_refresh and app_running_cache_value ~= nil and app_running_cache_time ~= nil then
if (now - app_running_cache_time) <= APP_RUNNING_CACHE_TTL_SECONDS then
callback(app_running_cache_value)
return
end
end
app_running_waiters[#app_running_waiters + 1] = callback
if app_running_check_inflight then
return
end
app_running_check_inflight = true
mp.command_native_async({
name = "subprocess",
args = process_scan_command(),
playback_only = false,
capture_stdout = true,
capture_stderr = false,
}, function(success, result)
app_running_check_inflight = false
local running = false
if success and result and result.status == 0 then
running = process_list_has_subminer(result.stdout)
end
app_running_cache_value = running
app_running_cache_time = now_seconds()
flush_app_running_waiters(running)
end)
end
local function is_subminer_app_running()
local running = is_subminer_process_running()
app_running_cache_value = running
app_running_cache_time = now_seconds()
return running
end
local function set_subminer_app_running_cache(running)
app_running_cache_value = running == true
app_running_cache_time = now_seconds()
end
local function detect_backend()
if detected_backend then
return detected_backend
end
local backend = nil
local subminer_log = ctx.log and ctx.log.subminer_log or function() end
if is_macos() then
backend = "macos"
elseif is_windows() then
backend = nil
elseif os.getenv("HYPRLAND_INSTANCE_SIGNATURE") then
backend = "hyprland"
elseif os.getenv("SWAYSOCK") then
backend = "sway"
elseif os.getenv("XDG_SESSION_TYPE") == "x11" or os.getenv("DISPLAY") then
backend = "x11"
else
subminer_log("warn", "backend", "Could not detect window manager, falling back to x11")
backend = "x11"
end
detected_backend = backend
if backend then
subminer_log("info", "backend", "Detected backend: " .. backend)
else
subminer_log("info", "backend", "No backend detected")
end
return backend
end
return {
is_windows = is_windows,
is_macos = is_macos,
is_linux = is_linux,
join_path = join_path,
default_socket_path = default_socket_path,
resolve_subminer_config_dir = resolve_subminer_config_dir,
resolve_session_bindings_artifact_path = resolve_session_bindings_artifact_path,
is_subminer_process_running = is_subminer_process_running,
is_subminer_app_running = is_subminer_app_running,
is_subminer_app_running_async = is_subminer_app_running_async,
set_subminer_app_running_cache = set_subminer_app_running_cache,
detect_backend = detect_backend,
}
end
return M