feat(launcher): Process.*, Http.save_to/received/expected, App.window_hide/show
Process.spawn/poll/kill/free - non-blocking child processes with no shell: posix_spawn on macOS (process.ll), CreateProcessW with MSVC-quoted arguments and no console window on Windows (process_win.ll), linked only when a program uses Process.*. Http.save_to streams a response body into a file (NSURLSession with a run-time delegate class on macOS, the WinHTTP read loop on Windows); Http.received / Http.expected report progress while it is pending. Freeing a pending request cancels it and parks the slot until the worker has finished. App.window_hide / App.window_show take the game's window off the screen and back without closing it; the run goes on while hidden. Docs, examples, tests and a changeset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c1db0d71ed
commit
43d5eb5b88
31 changed files with 89274 additions and 86727 deletions
|
|
@ -258,6 +258,14 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
if g_windowed { emit(` call void @lp_app_icon(ptr {ip})\n`) }
|
||||
return val("0", "void")
|
||||
}
|
||||
if (meth == "window_hide") or (meth == "window_show") {
|
||||
# Hide / show the game's own window (not the splash) without tearing down its
|
||||
# GL / Vulkan surface - a launcher steps aside while the game it started runs.
|
||||
# Headless there is no window, and the call lowers to nothing.
|
||||
use_pak()
|
||||
if g_windowed { emit(` call void @app_{meth}()\n`) }
|
||||
return val("0", "void")
|
||||
}
|
||||
perr(`unknown builtin App.{meth}`)
|
||||
}
|
||||
if (ns == "Pool") {
|
||||
|
|
@ -516,6 +524,9 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
if (meth == "header") { bare = "http_header_of"; push(labels, "handle"); push(labels, "name") }
|
||||
if (meth == "free") { bare = "http_close"; push(labels, "handle") }
|
||||
if (meth == "parse") { bare = "http_parse"; push(labels, "bytes"); push(labels, "len") }
|
||||
if (meth == "save_to") { bare = "http_save_to"; push(labels, "handle"); push(labels, "path") }
|
||||
if (meth == "received") { bare = "http_received"; push(labels, "handle") }
|
||||
if (meth == "expected") { bare = "http_expected"; push(labels, "handle") }
|
||||
}
|
||||
# Udp.* — polled IPv4 datagrams (runtime/native/udp.ludic + udp.ll / udp_win.ll).
|
||||
# Out-of-band like Http.*: the transport under a game's own netcode.
|
||||
|
|
@ -532,6 +543,14 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
if (meth == "ip") { bare = "udp_ip"; push(labels, "text") }
|
||||
if (meth == "ip_text") { bare = "udp_ip_text"; push(labels, "ip") }
|
||||
}
|
||||
# Process.* — child processes, started and polled (runtime/native/process.ludic +
|
||||
# process.ll / process_win.ll). Out-of-band, like Http.*.
|
||||
if (ns == "Process") {
|
||||
if (meth == "spawn") { bare = "process_spawn"; push(labels, "path"); push(labels, "args") }
|
||||
if (meth == "poll") { bare = "process_poll"; push(labels, "handle") }
|
||||
if (meth == "kill") { bare = "process_kill"; push(labels, "handle") }
|
||||
if (meth == "free") { bare = "process_free"; push(labels, "handle") }
|
||||
}
|
||||
# Phase 3: the bare reflection / networking / process builtins, namespaced.
|
||||
# Each is a pure alias — the callee is rewritten to the bare name below.
|
||||
if (ns == "World") {
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ function emit_pak_prelude() -> void {
|
|||
emith("declare void @splash_show(ptr, i32, i32)\n")
|
||||
emith("declare void @splash_hide()\n")
|
||||
emith("declare void @app_set_icon(ptr, i32)\n")
|
||||
emith("declare void @app_window_hide()\n")
|
||||
emith("declare void @app_window_show()\n")
|
||||
}
|
||||
|
||||
emit_pak_mount()
|
||||
|
|
|
|||
|
|
@ -254,6 +254,8 @@ function p_postfix() -> Node {
|
|||
if e.a.kind == E_ID and e.a.s == "Http" { g_uses_http = true }
|
||||
# Udp.* — any Udp method splices the datagram runtime (and links udp.ll / udp_win.ll).
|
||||
if e.a.kind == E_ID and e.a.s == "Udp" { g_uses_udp = true }
|
||||
# Process.* — any Process method splices the child-process runtime (process.ll / process_win.ll).
|
||||
if e.a.kind == E_ID and e.a.s == "Process" { g_uses_process = true }
|
||||
# Gl.* — any Gl method splices the OpenGL runtime (and links the GL backend).
|
||||
if e.a.kind == E_ID and e.a.s == "Gl" { g_uses_gl = true }
|
||||
# Vk.* — any Vk method splices the Vulkan runtime (and links its loader)
|
||||
|
|
@ -680,6 +682,7 @@ var g_uses_fx: bool = false # Fx.sparks/number/clear -> splice fx.ludic; f
|
|||
var g_uses_audio: bool = false # Audio.* (#22) -> splice audio.ludic; a windowed build also links audio.ll + AVFoundation
|
||||
var g_uses_http: bool = false # Http.* (#6) -> splice http.ludic; links http.ll + Foundation (macOS)
|
||||
var g_uses_udp: bool = false # Udp.* -> splice udp.ludic; links udp.ll (macOS) / udp_win.ll + ws2_32
|
||||
var g_uses_process: bool = false # Process.* -> splice process.ludic; links process.ll (macOS) / process_win.ll
|
||||
var g_uses_gl: bool = false # Gl.* -> splice gl.ludic (+ generated gl_api.ludic); links gl.ll + gl_thunks.ll + OpenGL
|
||||
var g_uses_vk: bool = false # Vk.* -> splice vk.ludic (+ generated vk_api.ludic); links vk_thunks.ll + the platform loader
|
||||
# issue #64: functions marked @System(Phase) in a prebuilt binary module — the
|
||||
|
|
@ -1197,6 +1200,13 @@ function maybe_splice_runtime() -> void {
|
|||
do_import("runtime/native/udp.ludic")
|
||||
cur_dir = saved
|
||||
}
|
||||
# Process.*: splice the child-process library. Spawning is native (process.ll /
|
||||
# process_win.ll); the argv array and the Windows command line are built in Ludic.
|
||||
if g_uses_process {
|
||||
cur_dir = ""
|
||||
do_import("runtime/native/process.ludic")
|
||||
cur_dir = saved
|
||||
}
|
||||
# Gl.*: splice the OpenGL surface (gl.ludic + the generated gl_api.ludic). The
|
||||
# native calls are the linked GL entry points themselves; the window attach is
|
||||
# is_windowed()-guarded, so a headless build renders into an offscreen context.
|
||||
|
|
|
|||
87167
selfhost/ludicc.seed.ll
87167
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -279,6 +279,12 @@ entry {
|
|||
if g_target_win { cmd = `{cmd} {join_path(home, "runtime/native/udp_win.ll")} -lws2_32` }
|
||||
else { cmd = `{cmd} {join_path(home, "runtime/native/udp.ll")}` }
|
||||
}
|
||||
# Process.* links the child-process layer: posix_spawn from libSystem on macOS,
|
||||
# CreateProcessW from kernel32 on Windows.
|
||||
if g_uses_process {
|
||||
if g_target_win { cmd = `{cmd} {join_path(home, "runtime/native/process_win.ll")}` }
|
||||
else { cmd = `{cmd} {join_path(home, "runtime/native/process.ll")}` }
|
||||
}
|
||||
if g_uses_http {
|
||||
if g_target_win {
|
||||
let httpw = join_path(home, "runtime/native/http_win.ll")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue