The Windows runtime keyed the held set by the character MapVirtualKeyA gave a virtual key, so a game's WASD belonged to whatever the active layout put there, and with an input method on every letter arrived as VK_PROCESSKEY. The typing block is now read from the scancode (the arrows, numpad and F-keys still by virtual key); macOS reads keyCode the same way. Input.key_label(key) names a key in the player's own layout (Windows) or as its US character elsewhere. Verified on Windows with SendInput into a live window: VK_Z carrying W's scancode holds 'w', VK_PROCESSKEY carrying A's holds 'a', Caps Lock changes nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
107 lines
8.5 KiB
Text
107 lines
8.5 KiB
Text
# emit_intrin2.ludic — the rest of the low-level intrinsics: raw memory, fixed
|
|
# memory access, pointer arithmetic, stdio, time, and the windowing hooks.
|
|
# Matches compiler/back/ir_intrin.c. Reached from emit_intrinsic's fall-through.
|
|
|
|
var g_windowed: bool = false # headless by default (games read stdin / dump PPM)
|
|
var g_target_win: bool = false # a Windows target: the header emits emit_win_prelude and the libSystem declares are skipped
|
|
|
|
# the IR that loads stdout (fd 1) or stderr (fd 2) as a FILE*. macOS names them as
|
|
# globals; the UCRT hands them out through a function (see emit_win.ludic).
|
|
function stdstream_rhs(fd: int) -> pointer {
|
|
if g_target_win { return `call ptr @lp_win_stdio(i32 {itoa(fd)})` }
|
|
if fd == 2 { return "load ptr, ptr @__stderrp" }
|
|
return "load ptr, ptr @__stdoutp"
|
|
}
|
|
|
|
function is_intrinsic2(name: pointer) -> bool {
|
|
if (name == "free") or (name == "fill") { return true }
|
|
if (name == "offset") or (name == "read_char") { return true }
|
|
if (name == "as_fixed") or (name == "as_int") { return true }
|
|
if (name == "is_windowed") or (name == "game_title") { return true }
|
|
if (name == "win_open") or (name == "win_poll") or (name == "win_present") { return true }
|
|
if (name == "win_running") or (name == "win_close") { return true }
|
|
if (name == "win_held") or (name == "win_mouse") { return true } # #50 device layer
|
|
if (name == "win_key_char") { return true } # Input.key_label
|
|
if (name == "win_pad") or (name == "win_touch") { return true } # #51 gamepad / touch
|
|
if (name == "win_cursor_mode") { return true } # #89 cursor capture
|
|
if (name == "snd_load") or (name == "snd_play") or (name == "snd_stop") { return true } # #22 audio
|
|
if (name == "snd_playing") or (name == "snd_set_volume") or (name == "snd_set_rate") or (name == "snd_set_pan") { return true }
|
|
if (name == "hs_req_new") or (name == "hs_req_header") or (name == "hs_req_body") { return true } # #6 HTTP
|
|
if (name == "hs_send") or (name == "hs_done") or (name == "hs_status") or (name == "hs_body") { return true }
|
|
if (name == "hs_blen") or (name == "hs_header") or (name == "hs_free") { return true }
|
|
return false
|
|
}
|
|
|
|
function emit_intrinsic2(name: pointer, e: Node) -> Val {
|
|
if (name == "free") {
|
|
let p = arg_code(e, 0); emit(" call void @free(ptr "); emit(p); emit(")\n"); return val("0", "void")
|
|
}
|
|
if (name == "fill") {
|
|
let p = arg_code(e, 0); let v = arg_code(e, 1); let n = arg_code(e, 2)
|
|
let w = emit_bind(`zext i32 {n} to i64`)
|
|
emit(" call ptr @memset(ptr "); emit(p); emit(", i32 "); emit(v); emit(", i64 "); emit(w); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "offset") {
|
|
let p = arg_code(e, 0); let n = arg_code(e, 1)
|
|
let g = nreg()
|
|
emit(" "); emit(g); emit(" = getelementptr inbounds i8, ptr "); emit(p); emit(", i32 "); emit(n); emit("\n")
|
|
return val(g, "pointer")
|
|
}
|
|
if (name == "read_char") { return val(emit_bind("call i32 @getchar()"), "int") }
|
|
if (name == "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }
|
|
if (name == "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
|
|
if (name == "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }
|
|
if (name == "game_title") { return val("@.gametitle", "string") }
|
|
# windowing hooks — declared external; only reached on the is_windowed() branch
|
|
if (name == "win_poll") { return val(emit_bind("call i32 @win_poll()"), "int") }
|
|
if (name == "win_running") { return val(emit_bind("call i32 @win_running()"), "bool") }
|
|
if (name == "win_open") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
|
emit(" call void @win_open(i32 "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", ptr "); emit(d); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "win_present") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
|
|
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "win_close") { emit(" call void @win_close()\n"); return val("0", "void") }
|
|
# #50 — fill a caller buffer with the platform device state (windowed only,
|
|
# DCE'd headless). win_held: an 8-word held-key bitset; win_mouse: [x, y,
|
|
# button-mask, wheel-delta].
|
|
if (name == "win_held") { let a = arg_code(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "win_mouse") { let a = arg_code(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# what the active keyboard layout types on a held-set key position (windowed only,
|
|
# DCE'd headless): a code point, 0 for none.
|
|
if (name == "win_key_char") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @win_key_char(i32 {a})`), "int") }
|
|
# #51 — win_pad: 6 words/pad [connected, mask, lx, ly, rx, ry]; win_touch: 3
|
|
# words/point [active, x, y]. Both windowed-only, DCE'd headless.
|
|
if (name == "win_pad") { let a = arg_code(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "win_touch") { let a = arg_code(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# #89 — set the OS cursor capture mode (windowed only; DCE'd headless).
|
|
if (name == "win_cursor_mode") { let a = arg_code(e, 0); emit(" call void @win_cursor_mode(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# #22 audio backend — AVAudioPlayer via audio.ll; windowed-only, DCE'd headless.
|
|
if (name == "snd_load") { let a = arg_code(e, 0); return val(emit_bind(`call ptr @snd_load(ptr {a})`), "pointer") }
|
|
if (name == "snd_play") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
|
emit(" call void @snd_play(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", i32 "); emit(d); emit(")\n"); return val("0", "void")
|
|
}
|
|
if (name == "snd_stop") { let a = arg_code(e, 0); emit(" call void @snd_stop(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_playing") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @snd_playing(ptr {a})`), "int") }
|
|
if (name == "snd_set_volume") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_volume(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_set_rate") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_rate(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_set_pan") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_pan(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
# #6 HTTP transport — NSURLConnection on a background thread (http.ll).
|
|
if (name == "hs_req_new") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_req_new(ptr {a}, ptr {b})`), "pointer") }
|
|
if (name == "hs_req_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_header(ptr "); emit(a); emit(", ptr "); emit(b); emit(", ptr "); emit(c); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_req_body") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_body(ptr "); emit(a); emit(", ptr "); emit(b); emit(", i32 "); emit(c); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_send") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @hs_send(i32 "); emit(a); emit(", ptr "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_done") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_done(i32 {a})`), "int") }
|
|
if (name == "hs_status") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_status(i32 {a})`), "int") }
|
|
if (name == "hs_body") { let a = arg_code(e, 0); return val(emit_bind(`call ptr @hs_body(i32 {a})`), "pointer") }
|
|
if (name == "hs_blen") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_blen(i32 {a})`), "int") }
|
|
if (name == "hs_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_header(i32 {a}, ptr {b})`), "pointer") }
|
|
if (name == "hs_free") { let a = arg_code(e, 0); emit(" call void @hs_free(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
|
return val("0", "void")
|
|
}
|