From 00d25dcc3e7e8c257bf10971c2846e6882d6bcdf Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Sun, 13 Sep 2026 02:33:53 +0300 Subject: [PATCH] feat(windows): a window - win32.ll, WGL on it, and a windowed Windows build win32.ll implements cocoa.ll's contracts over user32 and xinput: the message pump, the held-key set and frame key in Ludic codes, the mouse with raw input for cursor mode 2, clip-based cursor modes released on focus loss, XInput pads, and the software framebuffer present. win32_gl.ll puts the WGL 4.1 core context on that window (vsync, borderless full screen); gl_win.ll's context code is now lgl_wgl_core, shared with the headless hidden window, whose win_gl_* stubs move to gl_win_nowin.ll. audio_win.ll links Audio.* silently for now. Verified: macOS fixpoint, ludic-dev test 135/135, selfhost-test 32/32; on the PC gl_triangle renders identically headless and in a real window, and Maroon Lake builds windowed and runs a playtest to frame 240 in the desktop session. Co-Authored-By: Claude Opus 5 --- changes/windows-target.md | 11 +- runtime/native/audio_win.ll | 36 ++ runtime/native/gl_win.ll | 92 ++-- runtime/native/gl_win_nowin.ll | 44 ++ runtime/native/win32.ll | 867 +++++++++++++++++++++++++++++++++ runtime/native/win32_gl.ll | 253 ++++++++++ selfhost/ludicc.seed.ll | 802 ++++++++++++++++-------------- selfhost/main.ludic | 33 +- 8 files changed, 1696 insertions(+), 442 deletions(-) create mode 100644 runtime/native/audio_win.ll create mode 100644 runtime/native/gl_win_nowin.ll create mode 100644 runtime/native/win32.ll create mode 100644 runtime/native/win32_gl.ll diff --git a/changes/windows-target.md b/changes/windows-target.md index e0e4704f..d3036f98 100644 --- a/changes/windows-target.md +++ b/changes/windows-target.md @@ -16,8 +16,15 @@ type: feat - **The driver** — finds `C:\Program Files\LLVM\bin\clang.exe` when clang is not on `%PATH%`, writes `.exe` outputs, speaks cmd.exe for its directory and cleanup commands, and reads `argv[0]`, `%PATH%` and `$LUDIC_HOME` with either separator. -- **Not yet** — a window, `Audio.*` and `Http.*` are still macOS platform layers; - a Windows build that needs one stops with a message saying so. +- **A window** — `win32.ll` is `cocoa.ll`'s contract over user32: the held-key set and frame + key in Ludic codes, the mouse in framebuffer pixels with raw input for cursor mode 2, + cursor hide/lock/confine that lets go when the window loses the foreground, XInput pads + in SDL order with rescaled deadzones, and the software `win_present`. `win32_gl.ll` puts + the WGL context on that window with vsync and borderless full screen; a headless build + links `gl_win_nowin.ll` instead. The process is per-monitor DPI aware. +- **Not yet** — `Audio.*` links and is silent (`audio_win.ll`), the splash and + `App.set_icon` do nothing, and `Http.*` is still a macOS platform layer; a Windows build + that uses it stops with a message saying so. - **`Gl.*` on Windows** — `gl_win.ll` makes a WGL 4.1 core context on a hidden window's DC, and `gl_thunks_win.ll` (generated by `ludic-dev glgen` beside `gl_thunks.ll`) calls every entry point through a table `@lgl_win_load` fills from `wglGetProcAddress`, falling diff --git a/runtime/native/audio_win.ll b/runtime/native/audio_win.ll new file mode 100644 index 00000000..00af0fab --- /dev/null +++ b/runtime/native/audio_win.ll @@ -0,0 +1,36 @@ +; ============================================================================ +; audio_win.ll — the Windows sound backend (audio.ll's snd_* contract). +; +; Silent for now: every clip fails to load and every call is a no-op, which is the +; contract a headless build already runs on, so a windowed game links and plays +; without sound until the XAudio2 backend replaces this file. +; ============================================================================ + +define ptr @snd_load(ptr %path) { +entry: + ret ptr null +} +define void @snd_play(ptr %p, i32 %loops, i32 %rate, i32 %vol) { +entry: + ret void +} +define void @snd_stop(ptr %p) { +entry: + ret void +} +define i32 @snd_playing(ptr %p) { +entry: + ret i32 0 +} +define void @snd_set_volume(ptr %p, i32 %vol) { +entry: + ret void +} +define void @snd_set_rate(ptr %p, i32 %rate) { +entry: + ret void +} +define void @snd_set_pan(ptr %p, i32 %pan) { +entry: + ret void +} diff --git a/runtime/native/gl_win.ll b/runtime/native/gl_win.ll index 5e90cb78..ed789ed1 100644 --- a/runtime/native/gl_win.ll +++ b/runtime/native/gl_win.ll @@ -54,25 +54,16 @@ declare float @llvm.fabs.f32(float) @.wgl_attrs = private unnamed_addr constant [7 x i32] [i32 8337, i32 4, i32 8338, i32 1, i32 37158, i32 1, i32 0] @L_wgl_hwnd = internal global ptr null -@L_wgl_hdc = internal global ptr null @L_wgl_ctx = internal global ptr null -define i32 @cgl_offscreen() { +; lgl_wgl_core(hdc, alpha bits) -> context: a pixel format on the DC, a legacy context +; (wglCreateContextAttribsARB is only reachable through one), the 4.1 core context made +; current in its place, and the entry-point table filled. Null when any step fails. +; Shared by cgl_offscreen (a hidden window) and win32_gl.ll (the game's window). +define ptr @lgl_wgl_core(ptr %hdc, i32 %alpha) { entry: - %cur = load ptr, ptr @L_wgl_ctx - %have = icmp ne ptr %cur, null - br i1 %have, label %ok, label %win -win: - ; WS_POPUP, never shown - %w = call ptr @CreateWindowExA(i32 0, ptr @.wgl_class, ptr @.wgl_title, i32 -2147483648, i32 0, i32 0, i32 16, i32 16, ptr null, ptr null, ptr null, ptr null) - %wn = icmp eq ptr %w, null - br i1 %wn, label %fail, label %pf -pf: - store ptr %w, ptr @L_wgl_hwnd - %hdc = call ptr @GetDC(ptr %w) - store ptr %hdc, ptr @L_wgl_hdc ; PIXELFORMATDESCRIPTOR: nSize 40, nVersion 1, PFD_DRAW_TO_WINDOW|SUPPORT_OPENGL|DOUBLEBUFFER, - ; 32-bit colour, 24 depth, 8 stencil + ; 32-bit colour, alpha at 16, 24 depth, 8 stencil %pfd = alloca [40 x i8], align 4 call void @llvm.memset.p0.i64(ptr %pfd, i8 0, i64 40, i1 false) store i16 40, ptr %pfd @@ -82,6 +73,9 @@ pf: store i32 37, ptr %fl %cb = getelementptr i8, ptr %pfd, i64 9 store i8 32, ptr %cb + %ab = getelementptr i8, ptr %pfd, i64 16 + %a8 = trunc i32 %alpha to i8 + store i8 %a8, ptr %ab %db = getelementptr i8, ptr %pfd, i64 23 store i8 24, ptr %db %sb = getelementptr i8, ptr %pfd, i64 24 @@ -91,7 +85,6 @@ pf: %setz = icmp eq i32 %set, 0 br i1 %setz, label %fail, label %legacy legacy: - ; a legacy context first: wglCreateContextAttribsARB is only reachable through one %old = call ptr @wglCreateContext(ptr %hdc) %oldn = icmp eq ptr %old, null br i1 %oldn, label %fail, label %arb @@ -108,13 +101,35 @@ core: use: %m2 = call i32 @wglMakeCurrent(ptr %hdc, ptr %ctx) %d1 = call i32 @wglDeleteContext(ptr %old) - store ptr %ctx, ptr @L_wgl_ctx %n = call i32 @lgl_win_load() - br label %ok + ret ptr %ctx drop: %m3 = call i32 @wglMakeCurrent(ptr null, ptr null) %d2 = call i32 @wglDeleteContext(ptr %old) br label %fail +fail: + ret ptr null +} + +define i32 @cgl_offscreen() { +entry: + %cur = load ptr, ptr @L_wgl_ctx + %have = icmp ne ptr %cur, null + br i1 %have, label %ok, label %win +win: + ; WS_POPUP, never shown + %w = call ptr @CreateWindowExA(i32 0, ptr @.wgl_class, ptr @.wgl_title, i32 -2147483648, i32 0, i32 0, i32 16, i32 16, ptr null, ptr null, ptr null, ptr null) + %wn = icmp eq ptr %w, null + br i1 %wn, label %fail, label %ctx +ctx: + store ptr %w, ptr @L_wgl_hwnd + %hdc = call ptr @GetDC(ptr %w) + %c = call ptr @lgl_wgl_core(ptr %hdc, i32 0) + %cn = icmp eq ptr %c, null + br i1 %cn, label %fail, label %keep +keep: + store ptr %c, ptr @L_wgl_ctx + br label %ok ok: ret i32 1 fail: @@ -415,44 +430,3 @@ entry: %t = add i64 %sus, %frac ret i64 %t } - -; ---- the window half, until there is one ------------------------------------ -; gl.ludic names these on its is_windowed() branch. A headless build compiles that -; branch to nothing, but the names still have to resolve; a windowed Windows -; build will define them in the Win32 platform layer instead of here. -define i32 @win_gl_attach() { -entry: - ret i32 0 -} -define void @win_gl_resize(i32 %w, i32 %h) { -entry: - ret void -} -define void @win_gl_swap() { -entry: - ret void -} -define i32 @win_gl_scale() { -entry: - ret i32 1 -} -define void @win_gl_swap_interval(i32 %n) { -entry: - ret void -} -define void @win_gl_drawable(ptr %out) { -entry: - ret void -} -define void @win_gl_update() { -entry: - ret void -} -define void @win_gl_retina(i32 %on) { -entry: - ret void -} -define void @win_toggle_fullscreen() { -entry: - ret void -} diff --git a/runtime/native/gl_win_nowin.ll b/runtime/native/gl_win_nowin.ll new file mode 100644 index 00000000..84cf2f49 --- /dev/null +++ b/runtime/native/gl_win_nowin.ll @@ -0,0 +1,44 @@ +; ============================================================================ +; gl_win_nowin.ll — the window half of Gl.* in a headless Windows build. +; +; gl.ludic names win_gl_* on its is_windowed() branch. A headless build compiles that +; branch to nothing, but the names still have to resolve; a windowed build links +; win32_gl.ll, which defines them on the real window, in place of this file. +; ============================================================================ + +define i32 @win_gl_attach() { +entry: + ret i32 0 +} +define void @win_gl_resize(i32 %w, i32 %h) { +entry: + ret void +} +define void @win_gl_swap() { +entry: + ret void +} +define i32 @win_gl_scale() { +entry: + ret i32 1 +} +define void @win_gl_swap_interval(i32 %n) { +entry: + ret void +} +define void @win_gl_drawable(ptr %out) { +entry: + ret void +} +define void @win_gl_update() { +entry: + ret void +} +define void @win_gl_retina(i32 %on) { +entry: + ret void +} +define void @win_toggle_fullscreen() { +entry: + ret void +} diff --git a/runtime/native/win32.ll b/runtime/native/win32.ll new file mode 100644 index 00000000..3b9674cf --- /dev/null +++ b/runtime/native/win32.ll @@ -0,0 +1,867 @@ +; ============================================================================ +; win32.ll — the Windows window, written in LLVM IR. +; +; The counterpart of cocoa.ll, to the same contracts, spoken to user32 and +; xinput through their C ABI. ludicc links it into a windowed Windows binary; +; the GL half (win_gl_*) is win32_gl.ll, linked only when the program uses Gl.*, +; so a software-framebuffer game never names a WGL function. +; +; win_open(w, h, scale, title) win_poll() -> key win_present(fb, w, h) +; win_running() -> bool win_close() +; win_held(out) win_mouse(out) win_pad(out) win_touch(out) win_cursor_mode(m) +; splash_show(bytes, len, bg) splash_hide() app_set_icon(bytes, len) +; +; Units and conventions carried over from cocoa.ll, because the Ludic side reads +; them without knowing which platform filled them: +; +; held keys a 256-bit set of Ludic key codes: letters lowercase, digits and +; punctuation as their unshifted character, Space 32, Enter 10, +; Esc 27, Backspace 8, Tab 9, Shift 16, Ctrl 17, Alt 18, and the +; arrows at 128 129 130 131 (up down left right) +; frame key the last key pressed this frame, arrows reported as w s a d +; mouse framebuffer pixels (client pixels / scale), top-left origin; +; buttons bit0 left, bit1 right; wheel in notches, up positive +; pads SDL button order, Q16.16 axes with Y down +; +; The process is per-monitor DPI aware: a client pixel is a screen pixel, the +; backing scale is 1, and a 1280x720 window is 1280x720 pixels on any display. +; +; Keys are read from the message queue before dispatch rather than from WM_CHAR: +; TranslateMessage is never called, so no character messages are produced, and a +; key's character comes from MapVirtualKeyA. The system keys (Alt, F10) are never +; handed to DefWindowProc, whose menu loop would stall the game's frame loop - +; Alt+F4 is honoured here instead. +; ============================================================================ + +declare ptr @GetModuleHandleA(ptr) +declare i16 @RegisterClassExA(ptr) +declare ptr @CreateWindowExA(i32, ptr, ptr, i32, i32, i32, i32, i32, ptr, ptr, ptr, ptr) +declare i64 @DefWindowProcA(ptr, i32, i64, i64) +declare i32 @ShowWindow(ptr, i32) +declare i32 @SetForegroundWindow(ptr) +declare i32 @PeekMessageA(ptr, ptr, i32, i32, i32) +declare i64 @DispatchMessageA(ptr) +declare ptr @GetDC(ptr) +declare i32 @AdjustWindowRectEx(ptr, i32, i32, i32) +declare i32 @GetSystemMetrics(i32) +declare i32 @GetClientRect(ptr, ptr) +declare i32 @ClientToScreen(ptr, ptr) +declare i32 @ScreenToClient(ptr, ptr) +declare i32 @GetCursorPos(ptr) +declare i32 @ClipCursor(ptr) +declare ptr @SetCursor(ptr) +declare ptr @LoadCursorA(ptr, ptr) +declare ptr @GetForegroundWindow() +declare i32 @IsWindow(ptr) +declare ptr @SetCapture(ptr) +declare i32 @ReleaseCapture() +declare i32 @MapVirtualKeyA(i32, i32) +declare i16 @GetKeyState(i32) +declare i32 @RegisterRawInputDevices(ptr, i32, i32) +declare i32 @GetRawInputData(ptr, i32, ptr, ptr, i32) +declare i32 @SetProcessDpiAwarenessContext(ptr) +declare i32 @StretchDIBits(ptr, i32, i32, i32, i32, i32, i32, i32, i32, ptr, ptr, i32, i32) +declare void @Sleep(i32) +declare i32 @XInputGetState(i32, ptr) +declare void @llvm.memset.p0.i64(ptr, i8, i64, i1) + +@.w_class = private unnamed_addr constant [12 x i8] c"LudicWindow\00" + +; shared with win32_gl.ll, which declares them external +@W_hwnd = global ptr null +@W_hdc = global ptr null +@W_fbw = global i32 320 +@W_fbh = global i32 240 +@W_scale = global i32 3 + +@W_fb = internal global ptr null +@W_key = internal global i32 0 +@W_running = internal global i32 1 +@W_held = internal global [8 x i32] zeroinitializer +@W_mx = internal global i32 0 +@W_my = internal global i32 0 +@W_mbtn = internal global i32 0 +@W_wheel = internal global i32 0 +; raw mouse motion since the last win_mouse, for cursor mode 2 +@W_rawdx = internal global i32 0 +@W_rawdy = internal global i32 0 +; 0 normal, 1 hidden, 2 locked (relative), 3 confined; see win_cursor_maintain +@W_cursor_mode = internal global i32 0 +@W_cursor_hidden = internal global i32 0 +@W_clipped = internal global i32 0 + +; ---- the window procedure --------------------------------------------------- +; Only what is SENT to a window arrives here (posted input is read in win_poll). +define internal i64 @ludic_wndproc(ptr %hwnd, i32 %msg, i64 %wp, i64 %lp) { +entry: + switch i32 %msg, label %def [ + i32 16, label %close ; WM_CLOSE: end the run; the game decides what quitting does + i32 20, label %erase ; WM_ERASEBKGND: the frame covers it, and erasing flickers + i32 8, label %blur ; WM_KILLFOCUS + i32 32, label %setcur ; WM_SETCURSOR + ] +close: + store i32 0, ptr @W_running + ret i64 0 +erase: + ret i64 1 +blur: + ; a key held when focus went away never sends its key-up to this window: drop them all, + ; or the hiker walks on by himself after an Alt-Tab + call void @llvm.memset.p0.i64(ptr @W_held, i8 0, i64 32, i1 false) + store i32 0, ptr @W_mbtn + br label %def +setcur: + ; a hidden cursor is hidden by answering WM_SETCURSOR with no cursor over the client + ; area, which needs no ShowCursor counter to keep balanced + %hid = load i32, ptr @W_cursor_hidden + %ishid = icmp ne i32 %hid, 0 + %hit = and i64 %lp, 65535 + %client = icmp eq i64 %hit, 1 ; HTCLIENT + %both = and i1 %ishid, %client + br i1 %both, label %nocur, label %def +nocur: + %c = call ptr @SetCursor(ptr null) + ret i64 1 +def: + %r = call i64 @DefWindowProcA(ptr %hwnd, i32 %msg, i64 %wp, i64 %lp) + ret i64 %r +} + +define void @win_open(i32 %w, i32 %h, i32 %scale, ptr %title) { +entry: + %wc = alloca [80 x i8], align 8 + %rc = alloca [16 x i8], align 4 + %rid = alloca [16 x i8], align 8 + store i32 %w, ptr @W_fbw + store i32 %h, ptr @W_fbh + store i32 %scale, ptr @W_scale + %have = load ptr, ptr @W_hwnd + %again = icmp ne ptr %have, null + br i1 %again, label %out, label %make +make: + ; DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4 + %dpi = call i32 @SetProcessDpiAwarenessContext(ptr inttoptr (i64 -4 to ptr)) + %inst = call ptr @GetModuleHandleA(ptr null) + ; WNDCLASSEXA: cbSize 0, style 4, lpfnWndProc 8, hInstance 24, hCursor 40, lpszClassName 64 + call void @llvm.memset.p0.i64(ptr %wc, i8 0, i64 80, i1 false) + store i32 80, ptr %wc + %wst = getelementptr i8, ptr %wc, i64 4 + store i32 35, ptr %wst ; CS_OWNDC | CS_HREDRAW | CS_VREDRAW + %wwp = getelementptr i8, ptr %wc, i64 8 + store ptr @ludic_wndproc, ptr %wwp + %whi = getelementptr i8, ptr %wc, i64 24 + store ptr %inst, ptr %whi + %arrow = call ptr @LoadCursorA(ptr null, ptr inttoptr (i64 32512 to ptr)) ; IDC_ARROW + %whc = getelementptr i8, ptr %wc, i64 40 + store ptr %arrow, ptr %whc + %wcn = getelementptr i8, ptr %wc, i64 64 + store ptr @.w_class, ptr %wcn + %atom = call i16 @RegisterClassExA(ptr %wc) + ; the client area is w*scale x h*scale pixels; the frame goes round it + %pw = mul i32 %w, %scale + %ph = mul i32 %h, %scale + call void @llvm.memset.p0.i64(ptr %rc, i8 0, i64 16, i1 false) + %rcr = getelementptr i8, ptr %rc, i64 8 + store i32 %pw, ptr %rcr + %rcb = getelementptr i8, ptr %rc, i64 12 + store i32 %ph, ptr %rcb + %adj = call i32 @AdjustWindowRectEx(ptr %rc, i32 13565952, i32 0, i32 0) ; WS_OVERLAPPEDWINDOW + %rl = load i32, ptr %rc + %rtp = getelementptr i8, ptr %rc, i64 4 + %rt = load i32, ptr %rtp + %rr = load i32, ptr %rcr + %rb = load i32, ptr %rcb + %ww = sub i32 %rr, %rl + %wh = sub i32 %rb, %rt + ; centred on the primary display + %sw = call i32 @GetSystemMetrics(i32 0) + %sh = call i32 @GetSystemMetrics(i32 1) + %dx = sub i32 %sw, %ww + %x0 = sdiv i32 %dx, 2 + %xneg = icmp slt i32 %x0, 0 + %x = select i1 %xneg, i32 0, i32 %x0 + %dy = sub i32 %sh, %wh + %y0 = sdiv i32 %dy, 2 + %yneg = icmp slt i32 %y0, 0 + %y = select i1 %yneg, i32 0, i32 %y0 + ; WS_OVERLAPPEDWINDOW | WS_VISIBLE + %hwnd = call ptr @CreateWindowExA(i32 0, ptr @.w_class, ptr %title, i32 282001408, i32 %x, i32 %y, i32 %ww, i32 %wh, ptr null, ptr null, ptr %inst, ptr null) + %nowin = icmp eq ptr %hwnd, null + br i1 %nowin, label %out, label %show +show: + store ptr %hwnd, ptr @W_hwnd + %hdc = call ptr @GetDC(ptr %hwnd) + store ptr %hdc, ptr @W_hdc + %s1 = call i32 @ShowWindow(ptr %hwnd, i32 5) ; SW_SHOW + %s2 = call i32 @SetForegroundWindow(ptr %hwnd) + ; RAWINPUTDEVICE { usUsagePage 1, usUsage 2 (mouse), dwFlags 0, hwndTarget }: the raw + ; motion cursor mode 2 turns into mouse_dx / mouse_dy + call void @llvm.memset.p0.i64(ptr %rid, i8 0, i64 16, i1 false) + store i16 1, ptr %rid + %ru = getelementptr i8, ptr %rid, i64 2 + store i16 2, ptr %ru + %rh = getelementptr i8, ptr %rid, i64 8 + store ptr %hwnd, ptr %rh + %reg = call i32 @RegisterRawInputDevices(ptr %rid, i32 1, i32 16) + store i32 1, ptr @W_running + br label %out +out: + ret void +} + +; set (%on != 0) or clear a key's bit in the 256-bit held set +define void @win_held_bit(i32 %k, i32 %on) { +entry: + %lo = icmp slt i32 %k, 0 + %hi = icmp sgt i32 %k, 255 + %oob = or i1 %lo, %hi + br i1 %oob, label %ret, label %go +go: + %w = ashr i32 %k, 5 + %b = and i32 %k, 31 + %m = shl i32 1, %b + %p = getelementptr [8 x i32], ptr @W_held, i32 0, i32 %w + %cur = load i32, ptr %p + %onb = icmp ne i32 %on, 0 + br i1 %onb, label %set, label %clr +set: + %sv = or i32 %cur, %m + store i32 %sv, ptr %p + br label %ret +clr: + %nm = xor i32 %m, -1 + %cv = and i32 %cur, %nm + store i32 %cv, ptr %p + br label %ret +ret: + ret void +} + +; the unshifted character a virtual key types, 0 for none (F-keys, Home, ...) +define internal i32 @w_vk_char(i32 %vk) { +entry: + %c = call i32 @MapVirtualKeyA(i32 %vk, i32 2) ; MAPVK_VK_TO_CHAR; the top bit marks a dead key + %c7 = and i32 %c, 32767 + ret i32 %c7 +} + +; the held-set code of a virtual key (cocoa.ll's ev_keyval) +define internal i32 @w_keyval(i64 %vk64) { +entry: + %vk = trunc i64 %vk64 to i32 + switch i32 %vk, label %chars [ + i32 38, label %up + i32 40, label %down + i32 37, label %left + i32 39, label %right + i32 32, label %space + i32 13, label %enter + i32 27, label %esc + i32 16, label %shift + i32 17, label %ctrl + i32 18, label %alt + i32 8, label %bksp + i32 9, label %tab + ] +up: ret i32 128 +down: ret i32 129 +left: ret i32 130 +right: ret i32 131 +space: ret i32 32 +enter: ret i32 10 +esc: ret i32 27 +shift: ret i32 16 +ctrl: ret i32 17 +alt: ret i32 18 +bksp: ret i32 8 +tab: ret i32 9 +chars: + %c = call i32 @w_vk_char(i32 %vk) + ; the held set is keyed by the key, not the case: fold A-Z (Shift is its own code) + %isA = icmp sge i32 %c, 65 + %isZ = icmp sle i32 %c, 90 + %upper = and i1 %isA, %isZ + %lower = add i32 %c, 32 + %f = select i1 %upper, i32 %lower, i32 %c + ret i32 %f +} + +; the frame key of a virtual key (cocoa.ll's win_poll): arrows as wasd, a letter in the +; case Shift gives it, and 0 for a key that types nothing +define internal i32 @w_framekey(i64 %vk64) { +entry: + %vk = trunc i64 %vk64 to i32 + switch i32 %vk, label %chars [ + i32 38, label %up + i32 40, label %down + i32 37, label %left + i32 39, label %right + i32 32, label %space + i32 13, label %enter + i32 27, label %esc + i32 16, label %none + i32 17, label %none + i32 18, label %none + ] +up: ret i32 119 +down: ret i32 115 +left: ret i32 97 +right: ret i32 100 +space: ret i32 32 +enter: ret i32 10 +esc: ret i32 27 +none: ret i32 0 +chars: + %c = call i32 @w_vk_char(i32 %vk) + %isA = icmp sge i32 %c, 65 + %isZ = icmp sle i32 %c, 90 + %upper = and i1 %isA, %isZ + %ks = call i16 @GetKeyState(i32 16) + %shifted = icmp slt i16 %ks, 0 + %keepup = and i1 %upper, %shifted + %fold = xor i1 %keepup, true + %dofold = and i1 %upper, %fold + %lower = add i32 %c, 32 + %f = select i1 %dofold, i32 %lower, i32 %c + ret i32 %f +} + +; Drain the message queue: keys, buttons, the wheel and raw motion are read here, +; everything else is dispatched to the window procedure. Returns the frame key. +define i32 @win_poll() { +entry: + %msg = alloca [48 x i8], align 8 ; MSG: message 8, wParam 16, lParam 24 + %rbuf = alloca [48 x i8], align 8 ; RAWINPUT (mouse): header 24, RAWMOUSE 24 + %rsz = alloca i32, align 4 + store i32 0, ptr @W_key + %hwnd = load ptr, ptr @W_hwnd + %nohwnd = icmp eq ptr %hwnd, null + br i1 %nohwnd, label %done, label %pump +pump: + %got = call i32 @PeekMessageA(ptr %msg, ptr null, i32 0, i32 0, i32 1) ; PM_REMOVE + %any = icmp ne i32 %got, 0 + br i1 %any, label %handle, label %finish +handle: + %mp = getelementptr i8, ptr %msg, i64 8 + %m = load i32, ptr %mp + %wpp = getelementptr i8, ptr %msg, i64 16 + %wp = load i64, ptr %wpp + %lpp = getelementptr i8, ptr %msg, i64 24 + %lp = load i64, ptr %lpp + switch i32 %m, label %dispatch [ + i32 256, label %kdown ; WM_KEYDOWN + i32 260, label %kdown ; WM_SYSKEYDOWN + i32 257, label %kup ; WM_KEYUP + i32 261, label %kup ; WM_SYSKEYUP + i32 513, label %ldown ; WM_LBUTTONDOWN + i32 514, label %lup ; WM_LBUTTONUP + i32 516, label %rdown ; WM_RBUTTONDOWN + i32 517, label %rup ; WM_RBUTTONUP + i32 522, label %wheel ; WM_MOUSEWHEEL + i32 255, label %raw ; WM_INPUT + ] +kdown: + %hv = call i32 @w_keyval(i64 %wp) + call void @win_held_bit(i32 %hv, i32 1) + %isf4 = icmp eq i64 %wp, 115 ; VK_F4 + %issys = icmp eq i32 %m, 260 + %altf4 = and i1 %isf4, %issys + br i1 %altf4, label %quit, label %fkey +quit: + store i32 0, ptr @W_running + br label %pump +fkey: + %fk = call i32 @w_framekey(i64 %wp) + %hasfk = icmp ne i32 %fk, 0 + br i1 %hasfk, label %setfk, label %pump +setfk: + store i32 %fk, ptr @W_key + br label %pump +kup: + %uv = call i32 @w_keyval(i64 %wp) + call void @win_held_bit(i32 %uv, i32 0) + br label %pump +ldown: + %lb = load i32, ptr @W_mbtn + %lb2 = or i32 %lb, 1 + store i32 %lb2, ptr @W_mbtn + %cap1 = call ptr @SetCapture(ptr %hwnd) ; so the release is seen outside the window too + br label %dispatch +lup: + %lc = load i32, ptr @W_mbtn + %lc2 = and i32 %lc, -2 + store i32 %lc2, ptr @W_mbtn + br label %release +rdown: + %rbn = load i32, ptr @W_mbtn + %rbn2 = or i32 %rbn, 2 + store i32 %rbn2, ptr @W_mbtn + %cap2 = call ptr @SetCapture(ptr %hwnd) + br label %dispatch +rup: + %rcn = load i32, ptr @W_mbtn + %rcn2 = and i32 %rcn, -3 + store i32 %rcn2, ptr @W_mbtn + br label %release +release: + %still = load i32, ptr @W_mbtn + %nobtn = icmp eq i32 %still, 0 + br i1 %nobtn, label %uncap, label %dispatch +uncap: + %rel = call i32 @ReleaseCapture() + br label %dispatch +wheel: + ; the high word of wParam is the signed delta, 120 to a notch, positive away from the user + %hiw = lshr i64 %wp, 16 + %d16 = trunc i64 %hiw to i16 + %d32 = sext i16 %d16 to i32 + %notches = sdiv i32 %d32, 120 + %wv = load i32, ptr @W_wheel + %wv2 = add i32 %wv, %notches + store i32 %wv2, ptr @W_wheel + br label %pump +raw: + %hraw = inttoptr i64 %lp to ptr + store i32 48, ptr %rsz + %n = call i32 @GetRawInputData(ptr %hraw, i32 268435459, ptr %rbuf, ptr %rsz, i32 24) ; RID_INPUT + %okn = icmp sgt i32 %n, 43 + br i1 %okn, label %rawtype, label %dispatch +rawtype: + %rtype = load i32, ptr %rbuf + %ismouse = icmp eq i32 %rtype, 0 ; RIM_TYPEMOUSE + %flp = getelementptr i8, ptr %rbuf, i64 24 + %fl = load i16, ptr %flp + %abs = and i16 %fl, 1 ; MOUSE_MOVE_ABSOLUTE (a tablet, a remote desktop) + %isrel = icmp eq i16 %abs, 0 + %use = and i1 %ismouse, %isrel + br i1 %use, label %rawadd, label %dispatch +rawadd: + %rxp = getelementptr i8, ptr %rbuf, i64 36 + %rx = load i32, ptr %rxp + %ryp = getelementptr i8, ptr %rbuf, i64 40 + %ry = load i32, ptr %ryp + %ax = load i32, ptr @W_rawdx + %ax2 = add i32 %ax, %rx + store i32 %ax2, ptr @W_rawdx + %ay = load i32, ptr @W_rawdy + %ay2 = add i32 %ay, %ry + store i32 %ay2, ptr @W_rawdy + br label %dispatch +dispatch: + %dm = call i64 @DispatchMessageA(ptr %msg) + br label %pump +finish: + call void @win_cursor_maintain() + %alive = call i32 @IsWindow(ptr %hwnd) + %dead = icmp eq i32 %alive, 0 + br i1 %dead, label %closed, label %done +closed: + store i32 0, ptr @W_running + br label %done +done: + %k = load i32, ptr @W_key + ret i32 %k +} + +; A software framebuffer: w*h words of 0x00RRGGBB, row 0 at the top - which is +; exactly a top-down 32-bit DIB, so StretchDIBits takes it as it is. +define void @win_present(ptr %fb, i32 %w, i32 %h) { +entry: + %bmi = alloca [44 x i8], align 4 + store ptr %fb, ptr @W_fb + %hdc = load ptr, ptr @W_hdc + %nodc = icmp eq ptr %hdc, null + br i1 %nodc, label %out, label %draw +draw: + ; BITMAPINFOHEADER: biSize 40, biWidth, biHeight negative (top-down), 1 plane, 32 bpp, BI_RGB + call void @llvm.memset.p0.i64(ptr %bmi, i8 0, i64 44, i1 false) + store i32 40, ptr %bmi + %bw = getelementptr i8, ptr %bmi, i64 4 + store i32 %w, ptr %bw + %nh = sub i32 0, %h + %bh = getelementptr i8, ptr %bmi, i64 8 + store i32 %nh, ptr %bh + %bp = getelementptr i8, ptr %bmi, i64 12 + store i16 1, ptr %bp + %bb = getelementptr i8, ptr %bmi, i64 14 + store i16 32, ptr %bb + %sc = load i32, ptr @W_scale + %dw = mul i32 %w, %sc + %dh = mul i32 %h, %sc + %r = call i32 @StretchDIBits(ptr %hdc, i32 0, i32 0, i32 %dw, i32 %dh, i32 0, i32 0, i32 %w, i32 %h, ptr %fb, ptr %bmi, i32 0, i32 13369376) ; DIB_RGB_COLORS, SRCCOPY + call void @Sleep(i32 16) ; ~60 fps, as cocoa.ll's usleep + br label %out +out: + ret void +} + +define i32 @win_running() { +entry: + %r = load i32, ptr @W_running + ret i32 %r +} + +define void @win_close() { +entry: + store i32 0, ptr @W_cursor_mode + call void @win_cursor_maintain() + store i32 0, ptr @W_running + ret void +} + +define void @win_held(ptr %out) { +entry: + br label %loop +loop: + %i = phi i32 [ 0, %entry ], [ %ni, %body ] + %done = icmp sge i32 %i, 8 + br i1 %done, label %ret, label %body +body: + %sp = getelementptr [8 x i32], ptr @W_held, i32 0, i32 %i + %v = load i32, ptr %sp + %dp = getelementptr i32, ptr %out, i32 %i + store i32 %v, ptr %dp + %ni = add i32 %i, 1 + br label %loop +ret: + ret void +} + +; [x, y, buttons, wheel, raw dx, raw dy]; the wheel and the raw motion are per call +define void @win_mouse(ptr %out) { +entry: + %pt = alloca [8 x i8], align 4 + %mode = load i32, ptr @W_cursor_mode + %isrel = icmp eq i32 %mode, 2 + %ddx = load i32, ptr @W_rawdx + %ddy = load i32, ptr @W_rawdy + store i32 0, ptr @W_rawdx + store i32 0, ptr @W_rawdy + %fbw = load i32, ptr @W_fbw + %fbh = load i32, ptr @W_fbh + %fbwm = sub i32 %fbw, 1 + %fbhm = sub i32 %fbh, 1 + br i1 %isrel, label %rel, label %abs +rel: + ; locked: the OS cursor is held still, so the position is a virtual one moved by the + ; raw motion and clamped to the framebuffer; the motion itself is reported unclamped + %omx = load i32, ptr @W_mx + %omy = load i32, ptr @W_my + %nmx = add i32 %omx, %ddx + %nmy = add i32 %omy, %ddy + %p4r = getelementptr i32, ptr %out, i32 4 + store i32 %ddx, ptr %p4r + %p5r = getelementptr i32, ptr %out, i32 5 + store i32 %ddy, ptr %p5r + br label %clamp +abs: + %p4a = getelementptr i32, ptr %out, i32 4 + store i32 0, ptr %p4a + %p5a = getelementptr i32, ptr %out, i32 5 + store i32 0, ptr %p5a + %hwnd = load ptr, ptr @W_hwnd + %nowin = icmp eq ptr %hwnd, null + br i1 %nowin, label %emit, label %qpos +qpos: + %g = call i32 @GetCursorPos(ptr %pt) + %s = call i32 @ScreenToClient(ptr %hwnd, ptr %pt) + %cx = load i32, ptr %pt + %cyp = getelementptr i8, ptr %pt, i64 4 + %cy = load i32, ptr %cyp + %sc = load i32, ptr @W_scale + %sc0 = icmp slt i32 %sc, 1 + %scv = select i1 %sc0, i32 1, i32 %sc + %ax = sdiv i32 %cx, %scv + %ay = sdiv i32 %cy, %scv + br label %clamp +clamp: + %ux = phi i32 [ %nmx, %rel ], [ %ax, %qpos ] + %uy = phi i32 [ %nmy, %rel ], [ %ay, %qpos ] + %xlo = icmp slt i32 %ux, 0 + %x1 = select i1 %xlo, i32 0, i32 %ux + %xhi = icmp sgt i32 %x1, %fbwm + %x2 = select i1 %xhi, i32 %fbwm, i32 %x1 + %ylo = icmp slt i32 %uy, 0 + %y1 = select i1 %ylo, i32 0, i32 %uy + %yhi = icmp sgt i32 %y1, %fbhm + %y2 = select i1 %yhi, i32 %fbhm, i32 %y1 + store i32 %x2, ptr @W_mx + store i32 %y2, ptr @W_my + br label %emit +emit: + %mx = load i32, ptr @W_mx + store i32 %mx, ptr %out + %my = load i32, ptr @W_my + %p1 = getelementptr i32, ptr %out, i32 1 + store i32 %my, ptr %p1 + %mb = load i32, ptr @W_mbtn + %p2 = getelementptr i32, ptr %out, i32 2 + store i32 %mb, ptr %p2 + %wh = load i32, ptr @W_wheel + %p3 = getelementptr i32, ptr %out, i32 3 + store i32 %wh, ptr %p3 + store i32 0, ptr @W_wheel + ret void +} + +; ---- the cursor ---------------------------------------------------------------- +define internal void @w_cursor_hide(i32 %want) { +entry: + %cur = load i32, ptr @W_cursor_hidden + %same = icmp eq i32 %cur, %want + br i1 %same, label %out, label %change +change: + store i32 %want, ptr @W_cursor_hidden + %hide = icmp ne i32 %want, 0 + br i1 %hide, label %none, label %arrow +none: + %c0 = call ptr @SetCursor(ptr null) + br label %out +arrow: + %a = call ptr @LoadCursorA(ptr null, ptr inttoptr (i64 32512 to ptr)) + %c1 = call ptr @SetCursor(ptr %a) + br label %out +out: + ret void +} + +; Re-derive the cursor from the mode and whether this window is in front, every frame: +; hidden for modes 1-3, held to the window's centre for 2, to its client area for 3, +; and all of it let go the moment another window comes forward. ClipCursor is global, +; so it is only released when this window was the one that set it. +define void @win_cursor_maintain() { +entry: + %rc = alloca [16 x i8], align 4 + %hwnd = load ptr, ptr @W_hwnd + %nowin = icmp eq ptr %hwnd, null + br i1 %nowin, label %release, label %askfg +askfg: + %fg = call ptr @GetForegroundWindow() + %front = icmp eq ptr %fg, %hwnd + br i1 %front, label %keyed, label %release +release: + call void @w_cursor_hide(i32 0) + %was = load i32, ptr @W_clipped + %clipped = icmp ne i32 %was, 0 + br i1 %clipped, label %unclip, label %out +unclip: + %u = call i32 @ClipCursor(ptr null) + store i32 0, ptr @W_clipped + br label %out +keyed: + %mode = load i32, ptr @W_cursor_mode + %any = icmp ne i32 %mode, 0 + %anyi = zext i1 %any to i32 + call void @w_cursor_hide(i32 %anyi) + %is2 = icmp eq i32 %mode, 2 + %is3 = icmp eq i32 %mode, 3 + %holds = or i1 %is2, %is3 + br i1 %holds, label %rect, label %free +free: + %fwas = load i32, ptr @W_clipped + %fclipped = icmp ne i32 %fwas, 0 + br i1 %fclipped, label %unclip, label %out +rect: + %gc = call i32 @GetClientRect(ptr %hwnd, ptr %rc) + %tl = call i32 @ClientToScreen(ptr %hwnd, ptr %rc) + %brp = getelementptr i8, ptr %rc, i64 8 + %br = call i32 @ClientToScreen(ptr %hwnd, ptr %brp) + br i1 %is2, label %centre, label %clip +centre: + %l = load i32, ptr %rc + %tp = getelementptr i8, ptr %rc, i64 4 + %t = load i32, ptr %tp + %r = load i32, ptr %brp + %bp = getelementptr i8, ptr %rc, i64 12 + %b = load i32, ptr %bp + %lr = add i32 %l, %r + %cx = sdiv i32 %lr, 2 + %tb = add i32 %t, %b + %cy = sdiv i32 %tb, 2 + %cx1 = add i32 %cx, 1 + %cy1 = add i32 %cy, 1 + store i32 %cx, ptr %rc + store i32 %cy, ptr %tp + store i32 %cx1, ptr %brp + store i32 %cy1, ptr %bp + br label %clip +clip: + %cc = call i32 @ClipCursor(ptr %rc) + store i32 1, ptr @W_clipped + br label %out +out: + ret void +} + +define void @win_cursor_mode(i32 %mode) { +entry: + store i32 %mode, ptr @W_cursor_mode + call void @win_cursor_maintain() + ret void +} + +; ---- gamepads, through XInput -------------------------------------------------- +; A stick value past its deadzone, rescaled so the edge of the deadzone is 0.0 and +; full deflection is 1.0, as Q16.16 (GameController does the same before macOS sees it). +define internal i32 @w_axis(i16 %v, i32 %dz) { +entry: + %s = sext i16 %v to i32 + %neg = icmp slt i32 %s, 0 + %ms = sub i32 0, %s + %a = select i1 %neg, i32 %ms, i32 %s + %inside = icmp sle i32 %a, %dz + br i1 %inside, label %zero, label %scale +zero: + ret i32 0 +scale: + %past = sub i32 %a, %dz + %range = sub i32 32767, %dz + %p64 = sext i32 %past to i64 + %m = mul i64 %p64, 65536 + %r64 = sext i32 %range to i64 + %q = sdiv i64 %m, %r64 + %q32 = trunc i64 %q to i32 + %big = icmp sgt i32 %q32, 65536 + %cl = select i1 %big, i32 65536, i32 %q32 + %mneg = sub i32 0, %cl + %res = select i1 %neg, i32 %mneg, i32 %cl + ret i32 %res +} + +; bit %from of %bits, moved to bit %to +define internal i32 @w_bit(i32 %bits, i32 %from, i32 %to) { +entry: + %s = lshr i32 %bits, %from + %b = and i32 %s, 1 + %o = shl i32 %b, %to + ret i32 %o +} + +; [connected, mask, lx, ly, rx, ry] for pads 0-3. XINPUT_STATE: dwPacketNumber 0, then +; XINPUT_GAMEPAD at 4: wButtons 4, bLeftTrigger 6, bRightTrigger 7, sThumbLX 8, LY 10, RX 12, RY 14. +define void @win_pad(ptr %out) { +entry: + %st = alloca [16 x i8], align 4 + call void @llvm.memset.p0.i64(ptr %out, i8 0, i64 96, i1 false) + br label %loop +loop: + %p = phi i32 [ 0, %entry ], [ %pn, %next ] + %more = icmp slt i32 %p, 4 + br i1 %more, label %ask, label %done +ask: + call void @llvm.memset.p0.i64(ptr %st, i8 0, i64 16, i1 false) + %r = call i32 @XInputGetState(i32 %p, ptr %st) + %conn = icmp eq i32 %r, 0 ; ERROR_SUCCESS + br i1 %conn, label %pack, label %next +pack: + %base = mul i32 %p, 6 + %o0 = getelementptr i32, ptr %out, i32 %base + store i32 1, ptr %o0 + %btp = getelementptr i8, ptr %st, i64 4 + %bt16 = load i16, ptr %btp + %bt = zext i16 %bt16 to i32 + ; XInput bits -> SDL order: A B X Y Back . Start LStick RStick LShoulder RShoulder Up Down Left Right + %mA = call i32 @w_bit(i32 %bt, i32 12, i32 0) + %mB = call i32 @w_bit(i32 %bt, i32 13, i32 1) + %mX = call i32 @w_bit(i32 %bt, i32 14, i32 2) + %mY = call i32 @w_bit(i32 %bt, i32 15, i32 3) + %mBk = call i32 @w_bit(i32 %bt, i32 5, i32 4) + %mSt = call i32 @w_bit(i32 %bt, i32 4, i32 6) + %mLs = call i32 @w_bit(i32 %bt, i32 6, i32 7) + %mRs = call i32 @w_bit(i32 %bt, i32 7, i32 8) + %mLb = call i32 @w_bit(i32 %bt, i32 8, i32 9) + %mRb = call i32 @w_bit(i32 %bt, i32 9, i32 10) + %mU = call i32 @w_bit(i32 %bt, i32 0, i32 11) + %mD = call i32 @w_bit(i32 %bt, i32 1, i32 12) + %mL = call i32 @w_bit(i32 %bt, i32 2, i32 13) + %mR = call i32 @w_bit(i32 %bt, i32 3, i32 14) + ; the triggers are analogue; past XINPUT_GAMEPAD_TRIGGER_THRESHOLD (30) they are pressed + %ltp = getelementptr i8, ptr %st, i64 6 + %lt8 = load i8, ptr %ltp + %lt = zext i8 %lt8 to i32 + %ltd = icmp sgt i32 %lt, 30 + %ltb = zext i1 %ltd to i32 + %mLt = shl i32 %ltb, 15 + %rtp = getelementptr i8, ptr %st, i64 7 + %rt8 = load i8, ptr %rtp + %rt = zext i8 %rt8 to i32 + %rtd = icmp sgt i32 %rt, 30 + %rtb = zext i1 %rtd to i32 + %mRt = shl i32 %rtb, 16 + %k1 = or i32 %mA, %mB + %k2 = or i32 %k1, %mX + %k3 = or i32 %k2, %mY + %k4 = or i32 %k3, %mBk + %k5 = or i32 %k4, %mSt + %k6 = or i32 %k5, %mLs + %k7 = or i32 %k6, %mRs + %k8 = or i32 %k7, %mLb + %k9 = or i32 %k8, %mRb + %k10 = or i32 %k9, %mU + %k11 = or i32 %k10, %mD + %k12 = or i32 %k11, %mL + %k13 = or i32 %k12, %mR + %k14 = or i32 %k13, %mLt + %mask = or i32 %k14, %mRt + %i1 = add i32 %base, 1 + %o1 = getelementptr i32, ptr %out, i32 %i1 + store i32 %mask, ptr %o1 + ; XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849, RIGHT 8689; XInput's Y is up, Ludic's is down + %lxp = getelementptr i8, ptr %st, i64 8 + %lxv = load i16, ptr %lxp + %lx = call i32 @w_axis(i16 %lxv, i32 7849) + %i2 = add i32 %base, 2 + %o2 = getelementptr i32, ptr %out, i32 %i2 + store i32 %lx, ptr %o2 + %lyp = getelementptr i8, ptr %st, i64 10 + %lyv = load i16, ptr %lyp + %lyu = call i32 @w_axis(i16 %lyv, i32 7849) + %ly = sub i32 0, %lyu + %i3 = add i32 %base, 3 + %o3 = getelementptr i32, ptr %out, i32 %i3 + store i32 %ly, ptr %o3 + %rxp = getelementptr i8, ptr %st, i64 12 + %rxv = load i16, ptr %rxp + %rx = call i32 @w_axis(i16 %rxv, i32 8689) + %i4 = add i32 %base, 4 + %o4 = getelementptr i32, ptr %out, i32 %i4 + store i32 %rx, ptr %o4 + %ryp = getelementptr i8, ptr %st, i64 14 + %ryv = load i16, ptr %ryp + %ryu = call i32 @w_axis(i16 %ryv, i32 8689) + %ry = sub i32 0, %ryu + %i5 = add i32 %base, 5 + %o5 = getelementptr i32, ptr %out, i32 %i5 + store i32 %ry, ptr %o5 + br label %next +next: + %pn = add i32 %p, 1 + br label %loop +done: + ret void +} + +; Touch: a Windows PC reports its touchpad as a mouse, so there are no points. +define void @win_touch(ptr %out) { +entry: + call void @llvm.memset.p0.i64(ptr %out, i8 0, i64 96, i1 false) + ret void +} + +; ---- the splash and the icon --------------------------------------------------- +; Not yet: both need an image decoder (WIC), and on Windows the icon a player sees on +; the taskbar and in Explorer is the one linked into the .exe, which `ludic bundle` +; will do. Until then these keep the contract - safe to call, change nothing. +define void @splash_show(ptr %bytes, i32 %len, i32 %bg) { +entry: + ret void +} +define void @splash_hide() { +entry: + ret void +} +define void @app_set_icon(ptr %bytes, i32 %len) { +entry: + ret void +} diff --git a/runtime/native/win32_gl.ll b/runtime/native/win32_gl.ll new file mode 100644 index 00000000..ee526f79 --- /dev/null +++ b/runtime/native/win32_gl.ll @@ -0,0 +1,253 @@ +; ============================================================================ +; win32_gl.ll — OpenGL on the Windows window (cocoa.ll's win_gl_* half). +; +; Linked into a windowed Windows program that uses Gl.*, beside win32.ll (the +; window) and gl_win.ll (which makes a 4.1 core context on a DC and fills the +; entry-point table). A headless build links gl_win_nowin.ll in its place. +; +; win_gl_attach() -> ok the context on the window's DC (0 = no window yet) +; win_gl_resize(w, h) client area of w x h pixels, centred +; win_gl_swap() SwapBuffers, paced by the swap interval +; win_gl_scale() -> 1 the process is DPI aware: one pixel is one pixel +; win_gl_drawable(out) the client area in pixels +; win_toggle_fullscreen() borderless, over the monitor the window is on +; ============================================================================ + +@W_hwnd = external global ptr +@W_hdc = external global ptr +@W_fbw = external global i32 +@W_fbh = external global i32 +@W_scale = external global i32 + +declare ptr @lgl_wgl_core(ptr, i32) +declare ptr @wglGetProcAddress(ptr) +declare i32 @SwapBuffers(ptr) +declare i32 @GetClientRect(ptr, ptr) +declare i32 @AdjustWindowRectEx(ptr, i32, i32, i32) +declare i32 @SetWindowPos(ptr, ptr, i32, i32, i32, i32, i32) +declare i32 @GetSystemMetrics(i32) +declare i64 @GetWindowLongPtrA(ptr, i32) +declare i64 @SetWindowLongPtrA(ptr, i32, i64) +declare i32 @GetWindowRect(ptr, ptr) +declare ptr @MonitorFromWindow(ptr, i32) +declare i32 @GetMonitorInfoA(ptr, ptr) +declare void @llvm.memset.p0.i64(ptr, i8, i64, i1) + +@.wgl_swapint = private unnamed_addr constant [19 x i8] c"wglSwapIntervalEXT\00" + +@W_glctx = internal global ptr null +@W_fs = internal global i32 0 +@W_fs_style = internal global i64 0 +@W_fs_rect = internal global [4 x i32] zeroinitializer + +define i32 @win_gl_attach() { +entry: + %have = load ptr, ptr @W_glctx + %again = icmp ne ptr %have, null + br i1 %again, label %ok, label %make +make: + %hdc = load ptr, ptr @W_hdc + %nodc = icmp eq ptr %hdc, null + br i1 %nodc, label %fail, label %ctx +ctx: + ; 24-bit colour with 8 of alpha, 24 depth, 8 stencil, double-buffered: cocoa.ll's format + %c = call ptr @lgl_wgl_core(ptr %hdc, i32 8) + %noc = icmp eq ptr %c, null + br i1 %noc, label %fail, label %keep +keep: + store ptr %c, ptr @W_glctx + call void @win_gl_swap_interval(i32 1) + br label %ok +ok: + ret i32 1 +fail: + ret i32 0 +} + +define void @win_gl_resize(i32 %w, i32 %h) { +entry: + %rc = alloca [16 x i8], align 4 + %hwnd = load ptr, ptr @W_hwnd + %nowin = icmp eq ptr %hwnd, null + br i1 %nowin, label %out, label %go +go: + store i32 %w, ptr @W_fbw + store i32 %h, ptr @W_fbh + store i32 1, ptr @W_scale + %fs = load i32, ptr @W_fs + %isfs = icmp ne i32 %fs, 0 + br i1 %isfs, label %out, label %size +size: + call void @llvm.memset.p0.i64(ptr %rc, i8 0, i64 16, i1 false) + %rcr = getelementptr i8, ptr %rc, i64 8 + store i32 %w, ptr %rcr + %rcb = getelementptr i8, ptr %rc, i64 12 + store i32 %h, ptr %rcb + %style64 = call i64 @GetWindowLongPtrA(ptr %hwnd, i32 -16) ; GWL_STYLE + %style = trunc i64 %style64 to i32 + %adj = call i32 @AdjustWindowRectEx(ptr %rc, i32 %style, i32 0, i32 0) + %l = load i32, ptr %rc + %tp = getelementptr i8, ptr %rc, i64 4 + %t = load i32, ptr %tp + %r = load i32, ptr %rcr + %b = load i32, ptr %rcb + %ww = sub i32 %r, %l + %wh = sub i32 %b, %t + %sw = call i32 @GetSystemMetrics(i32 0) + %sh = call i32 @GetSystemMetrics(i32 1) + %dx = sub i32 %sw, %ww + %x0 = sdiv i32 %dx, 2 + %xneg = icmp slt i32 %x0, 0 + %x = select i1 %xneg, i32 0, i32 %x0 + %dy = sub i32 %sh, %wh + %y0 = sdiv i32 %dy, 2 + %yneg = icmp slt i32 %y0, 0 + %y = select i1 %yneg, i32 0, i32 %y0 + %sp = call i32 @SetWindowPos(ptr %hwnd, ptr null, i32 %x, i32 %y, i32 %ww, i32 %wh, i32 20) ; SWP_NOZORDER | SWP_NOACTIVATE + br label %out +out: + ret void +} + +define void @win_gl_swap() { +entry: + %c = load ptr, ptr @W_glctx + %noc = icmp eq ptr %c, null + br i1 %noc, label %out, label %go +go: + %hdc = load ptr, ptr @W_hdc + %r = call i32 @SwapBuffers(ptr %hdc) + br label %out +out: + ret void +} + +define i32 @win_gl_scale() { +entry: + ret i32 1 +} + +; 1 = vsync (set at attach), 0 = free-running; wglSwapIntervalEXT is a driver extension, +; reachable only through the current context +define void @win_gl_swap_interval(i32 %n) { +entry: + %c = load ptr, ptr @W_glctx + %noc = icmp eq ptr %c, null + br i1 %noc, label %out, label %find +find: + %fp = call ptr @wglGetProcAddress(ptr @.wgl_swapint) + %fpi = ptrtoint ptr %fp to i64 + %fpa = add i64 %fpi, 1 + %bad = icmp ult i64 %fpa, 5 + br i1 %bad, label %out, label %call +call: + %r = call i32 %fp(i32 %n) + br label %out +out: + ret void +} + +define void @win_gl_drawable(ptr %out) { +entry: + %rc = alloca [16 x i8], align 4 + %hwnd = load ptr, ptr @W_hwnd + %nowin = icmp eq ptr %hwnd, null + br i1 %nowin, label %none, label %go +go: + %g = call i32 @GetClientRect(ptr %hwnd, ptr %rc) + %rp = getelementptr i8, ptr %rc, i64 8 + %w = load i32, ptr %rp + %bp = getelementptr i8, ptr %rc, i64 12 + %h = load i32, ptr %bp + store i32 %w, ptr %out + %o1 = getelementptr i32, ptr %out, i32 1 + store i32 %h, ptr %o1 + ; a minimised window has no client area; keep the last real size for the mouse mapping + %zw = icmp sle i32 %w, 0 + %zh = icmp sle i32 %h, 0 + %gone = or i1 %zw, %zh + br i1 %gone, label %done, label %track +track: + store i32 %w, ptr @W_fbw + store i32 %h, ptr @W_fbh + br label %done +done: + ret void +none: + store i32 0, ptr %out + %o1n = getelementptr i32, ptr %out, i32 1 + store i32 0, ptr %o1n + ret void +} + +; WGL needs no notice that its window changed size +define void @win_gl_update() { +entry: + ret void +} + +; The Retina switch: on Windows the process is DPI aware and always draws one pixel per +; screen pixel, which is what "on" means on macOS; there is no lower-resolution surface +; for "off" to fall back to, so it changes nothing. +define void @win_gl_retina(i32 %on) { +entry: + ret void +} + +; Borderless full screen over the window's monitor, and back to the frame and the +; rectangle it had. (Exclusive full screen would change the display mode under a +; player's other windows; nothing in the game needs it.) +define void @win_toggle_fullscreen() { +entry: + %mi = alloca [40 x i8], align 4 + %hwnd = load ptr, ptr @W_hwnd + %nowin = icmp eq ptr %hwnd, null + br i1 %nowin, label %out, label %which +which: + %fs = load i32, ptr @W_fs + %isfs = icmp ne i32 %fs, 0 + br i1 %isfs, label %leave, label %enter +enter: + %style = call i64 @GetWindowLongPtrA(ptr %hwnd, i32 -16) + store i64 %style, ptr @W_fs_style + %gr = call i32 @GetWindowRect(ptr %hwnd, ptr @W_fs_rect) + %mon = call ptr @MonitorFromWindow(ptr %hwnd, i32 2) ; MONITOR_DEFAULTTONEAREST + call void @llvm.memset.p0.i64(ptr %mi, i8 0, i64 40, i1 false) + store i32 40, ptr %mi + %gm = call i32 @GetMonitorInfoA(ptr %mon, ptr %mi) + ; MONITORINFO.rcMonitor at 4 + %mlp = getelementptr i8, ptr %mi, i64 4 + %ml = load i32, ptr %mlp + %mtp = getelementptr i8, ptr %mi, i64 8 + %mt = load i32, ptr %mtp + %mrp = getelementptr i8, ptr %mi, i64 12 + %mr = load i32, ptr %mrp + %mbp = getelementptr i8, ptr %mi, i64 16 + %mb = load i32, ptr %mbp + ; drop WS_OVERLAPPEDWINDOW (0x00CF0000), add WS_POPUP (0x80000000) + %bare = and i64 %style, -13565953 + %popup = or i64 %bare, 2147483648 + %sl = call i64 @SetWindowLongPtrA(ptr %hwnd, i32 -16, i64 %popup) + %fw = sub i32 %mr, %ml + %fh = sub i32 %mb, %mt + %sp = call i32 @SetWindowPos(ptr %hwnd, ptr null, i32 %ml, i32 %mt, i32 %fw, i32 %fh, i32 544) ; SWP_FRAMECHANGED | SWP_NOOWNERZORDER + store i32 1, ptr @W_fs + br label %out +leave: + %saved = load i64, ptr @W_fs_style + %sl2 = call i64 @SetWindowLongPtrA(ptr %hwnd, i32 -16, i64 %saved) + %l = load i32, ptr @W_fs_rect + %tp = getelementptr i32, ptr @W_fs_rect, i32 1 + %t = load i32, ptr %tp + %rp = getelementptr i32, ptr @W_fs_rect, i32 2 + %r = load i32, ptr %rp + %bp = getelementptr i32, ptr @W_fs_rect, i32 3 + %b = load i32, ptr %bp + %ww = sub i32 %r, %l + %wh = sub i32 %b, %t + %sp2 = call i32 @SetWindowPos(ptr %hwnd, ptr null, i32 %l, i32 %t, i32 %ww, i32 %wh, i32 548) ; ... | SWP_NOZORDER + store i32 0, ptr @W_fs + br label %out +out: + ret void +} diff --git a/selfhost/ludicc.seed.ll b/selfhost/ludicc.seed.ll index 6bbdb6d6..8c3dbba4 100644 --- a/selfhost/ludicc.seed.ll +++ b/selfhost/ludicc.seed.ll @@ -11314,40 +11314,47 @@ declare void @hs_free(i32) @.str11126 = private unnamed_addr constant [108 x i8] c"usage: ludicc [-o out] [--windowed|--headless] [--target triple] [--emit-llvm] [--save-temps]\0A\00" @.str11127 = private unnamed_addr constant [8 x i8] c"windows\00" @.str11128 = private unnamed_addr constant [27 x i8] c"ludicc: cannot open input\0A\00" -@.str11129 = private unnamed_addr constant [81 x i8] c"ludicc: a windowed build is not available on Windows yet; build with --headless\0A\00" -@.str11130 = private unnamed_addr constant [48 x i8] c"ludicc: Http.* is not available on Windows yet\0A\00" -@.str11131 = private unnamed_addr constant [5 x i8] c"TEMP\00" -@.str11132 = private unnamed_addr constant [2 x i8] c".\00" -@.str11133 = private unnamed_addr constant [12 x i8] c"/ludic-run-\00" -@.str11134 = private unnamed_addr constant [16 x i8] c"/tmp/ludic-run-\00" +@.str11129 = private unnamed_addr constant [48 x i8] c"ludicc: Http.* is not available on Windows yet\0A\00" +@.str11130 = private unnamed_addr constant [5 x i8] c"TEMP\00" +@.str11131 = private unnamed_addr constant [2 x i8] c".\00" +@.str11132 = private unnamed_addr constant [12 x i8] c"/ludic-run-\00" +@.str11133 = private unnamed_addr constant [16 x i8] c"/tmp/ludic-run-\00" +@.str11134 = private unnamed_addr constant [5 x i8] c".exe\00" @.str11135 = private unnamed_addr constant [5 x i8] c".exe\00" -@.str11136 = private unnamed_addr constant [5 x i8] c".exe\00" -@.str11137 = private unnamed_addr constant [4 x i8] c".ll\00" -@.str11138 = private unnamed_addr constant [25 x i8] c"ludicc: cannot write IR\0A\00" -@.str11139 = private unnamed_addr constant [27 x i8] c" -O2 -Wno-override-module \00" -@.str11140 = private unnamed_addr constant [24 x i8] c"runtime/native/cocoa.ll\00" -@.str11141 = private unnamed_addr constant [2 x i8] c" \00" -@.str11142 = private unnamed_addr constant [79 x i8] c" -framework Cocoa -Wl,-needed_framework,GameController -Wl,-rpath,@loader_path\00" -@.str11143 = private unnamed_addr constant [24 x i8] c"runtime/native/audio.ll\00" -@.str11144 = private unnamed_addr constant [2 x i8] c" \00" -@.str11145 = private unnamed_addr constant [36 x i8] c" -Wl,-needed_framework,AVFoundation\00" -@.str11146 = private unnamed_addr constant [23 x i8] c"runtime/native/http.ll\00" -@.str11147 = private unnamed_addr constant [2 x i8] c" \00" -@.str11148 = private unnamed_addr constant [34 x i8] c" -Wl,-needed_framework,Foundation\00" -@.str11149 = private unnamed_addr constant [25 x i8] c"runtime/native/gl_win.ll\00" -@.str11150 = private unnamed_addr constant [32 x i8] c"runtime/native/gl_thunks_win.ll\00" +@.str11136 = private unnamed_addr constant [4 x i8] c".ll\00" +@.str11137 = private unnamed_addr constant [25 x i8] c"ludicc: cannot write IR\0A\00" +@.str11138 = private unnamed_addr constant [27 x i8] c" -O2 -Wno-override-module \00" +@.str11139 = private unnamed_addr constant [24 x i8] c"runtime/native/win32.ll\00" +@.str11140 = private unnamed_addr constant [2 x i8] c" \00" +@.str11141 = private unnamed_addr constant [27 x i8] c" -luser32 -lgdi32 -lxinput\00" +@.str11142 = private unnamed_addr constant [28 x i8] c"runtime/native/audio_win.ll\00" +@.str11143 = private unnamed_addr constant [2 x i8] c" \00" +@.str11144 = private unnamed_addr constant [24 x i8] c"runtime/native/cocoa.ll\00" +@.str11145 = private unnamed_addr constant [2 x i8] c" \00" +@.str11146 = private unnamed_addr constant [79 x i8] c" -framework Cocoa -Wl,-needed_framework,GameController -Wl,-rpath,@loader_path\00" +@.str11147 = private unnamed_addr constant [24 x i8] c"runtime/native/audio.ll\00" +@.str11148 = private unnamed_addr constant [2 x i8] c" \00" +@.str11149 = private unnamed_addr constant [36 x i8] c" -Wl,-needed_framework,AVFoundation\00" +@.str11150 = private unnamed_addr constant [23 x i8] c"runtime/native/http.ll\00" @.str11151 = private unnamed_addr constant [2 x i8] c" \00" -@.str11152 = private unnamed_addr constant [2 x i8] c" \00" -@.str11153 = private unnamed_addr constant [29 x i8] c" -luser32 -lgdi32 -lopengl32\00" -@.str11154 = private unnamed_addr constant [21 x i8] c"runtime/native/gl.ll\00" -@.str11155 = private unnamed_addr constant [28 x i8] c"runtime/native/gl_thunks.ll\00" -@.str11156 = private unnamed_addr constant [2 x i8] c" \00" +@.str11152 = private unnamed_addr constant [34 x i8] c" -Wl,-needed_framework,Foundation\00" +@.str11153 = private unnamed_addr constant [25 x i8] c"runtime/native/gl_win.ll\00" +@.str11154 = private unnamed_addr constant [32 x i8] c"runtime/native/gl_thunks_win.ll\00" +@.str11155 = private unnamed_addr constant [31 x i8] c"runtime/native/gl_win_nowin.ll\00" +@.str11156 = private unnamed_addr constant [27 x i8] c"runtime/native/win32_gl.ll\00" @.str11157 = private unnamed_addr constant [2 x i8] c" \00" -@.str11158 = private unnamed_addr constant [19 x i8] c" -framework OpenGL\00" -@.str11159 = private unnamed_addr constant [5 x i8] c" -o \00" -@.str11160 = private unnamed_addr constant [21 x i8] c"ludicc: link failed\0A\00" -@.str11161 = private unnamed_addr constant [2 x i8] c"\22\00" -@.str11162 = private unnamed_addr constant [2 x i8] c"\22\00" +@.str11158 = private unnamed_addr constant [2 x i8] c" \00" +@.str11159 = private unnamed_addr constant [2 x i8] c" \00" +@.str11160 = private unnamed_addr constant [29 x i8] c" -luser32 -lgdi32 -lopengl32\00" +@.str11161 = private unnamed_addr constant [21 x i8] c"runtime/native/gl.ll\00" +@.str11162 = private unnamed_addr constant [28 x i8] c"runtime/native/gl_thunks.ll\00" +@.str11163 = private unnamed_addr constant [2 x i8] c" \00" +@.str11164 = private unnamed_addr constant [2 x i8] c" \00" +@.str11165 = private unnamed_addr constant [19 x i8] c" -framework OpenGL\00" +@.str11166 = private unnamed_addr constant [5 x i8] c" -o \00" +@.str11167 = private unnamed_addr constant [21 x i8] c"ludicc: link failed\0A\00" +@.str11168 = private unnamed_addr constant [2 x i8] c"\22\00" +@.str11169 = private unnamed_addr constant [2 x i8] c"\22\00" define i32 @lp_str_eq(ptr %a, ptr %b) { entry: br label %loop @@ -12152,56 +12159,56 @@ sod: store i32 %fcnt, ptr %d2 ret ptr %h } -@.str11163 = private unnamed_addr constant [4 x i8] c"png\00" -@.str11164 = private unnamed_addr constant [10 x i8] c"image/png\00" -@.str11165 = private unnamed_addr constant [4 x i8] c"jpg\00" -@.str11166 = private unnamed_addr constant [11 x i8] c"image/jpeg\00" -@.str11167 = private unnamed_addr constant [5 x i8] c"jpeg\00" -@.str11168 = private unnamed_addr constant [11 x i8] c"image/jpeg\00" -@.str11169 = private unnamed_addr constant [4 x i8] c"gif\00" -@.str11170 = private unnamed_addr constant [10 x i8] c"image/gif\00" -@.str11171 = private unnamed_addr constant [4 x i8] c"bmp\00" -@.str11172 = private unnamed_addr constant [10 x i8] c"image/bmp\00" -@.str11173 = private unnamed_addr constant [5 x i8] c"webp\00" -@.str11174 = private unnamed_addr constant [11 x i8] c"image/webp\00" -@.str11175 = private unnamed_addr constant [4 x i8] c"svg\00" -@.str11176 = private unnamed_addr constant [14 x i8] c"image/svg+xml\00" -@.str11177 = private unnamed_addr constant [4 x i8] c"txt\00" -@.str11178 = private unnamed_addr constant [11 x i8] c"text/plain\00" -@.str11179 = private unnamed_addr constant [3 x i8] c"md\00" -@.str11180 = private unnamed_addr constant [14 x i8] c"text/markdown\00" -@.str11181 = private unnamed_addr constant [4 x i8] c"csv\00" -@.str11182 = private unnamed_addr constant [9 x i8] c"text/csv\00" -@.str11183 = private unnamed_addr constant [5 x i8] c"html\00" -@.str11184 = private unnamed_addr constant [10 x i8] c"text/html\00" -@.str11185 = private unnamed_addr constant [4 x i8] c"htm\00" -@.str11186 = private unnamed_addr constant [10 x i8] c"text/html\00" -@.str11187 = private unnamed_addr constant [4 x i8] c"css\00" -@.str11188 = private unnamed_addr constant [9 x i8] c"text/css\00" -@.str11189 = private unnamed_addr constant [3 x i8] c"js\00" -@.str11190 = private unnamed_addr constant [16 x i8] c"text/javascript\00" -@.str11191 = private unnamed_addr constant [5 x i8] c"json\00" -@.str11192 = private unnamed_addr constant [17 x i8] c"application/json\00" -@.str11193 = private unnamed_addr constant [4 x i8] c"xml\00" -@.str11194 = private unnamed_addr constant [16 x i8] c"application/xml\00" -@.str11195 = private unnamed_addr constant [4 x i8] c"zip\00" -@.str11196 = private unnamed_addr constant [16 x i8] c"application/zip\00" -@.str11197 = private unnamed_addr constant [4 x i8] c"pdf\00" -@.str11198 = private unnamed_addr constant [16 x i8] c"application/pdf\00" -@.str11199 = private unnamed_addr constant [4 x i8] c"wav\00" -@.str11200 = private unnamed_addr constant [10 x i8] c"audio/wav\00" -@.str11201 = private unnamed_addr constant [4 x i8] c"ogg\00" -@.str11202 = private unnamed_addr constant [10 x i8] c"audio/ogg\00" -@.str11203 = private unnamed_addr constant [4 x i8] c"mp3\00" -@.str11204 = private unnamed_addr constant [11 x i8] c"audio/mpeg\00" -@.str11205 = private unnamed_addr constant [4 x i8] c"ttf\00" -@.str11206 = private unnamed_addr constant [9 x i8] c"font/ttf\00" -@.str11207 = private unnamed_addr constant [4 x i8] c"otf\00" -@.str11208 = private unnamed_addr constant [9 x i8] c"font/otf\00" -@.str11209 = private unnamed_addr constant [6 x i8] c"ludic\00" -@.str11210 = private unnamed_addr constant [13 x i8] c"text/x-ludic\00" -@.str11211 = private unnamed_addr constant [25 x i8] c"application/octet-stream\00" -@mime_tbl = private unnamed_addr constant [24 x { ptr, ptr }] [{ ptr, ptr } { ptr @.str11163, ptr @.str11164 }, { ptr, ptr } { ptr @.str11165, ptr @.str11166 }, { ptr, ptr } { ptr @.str11167, ptr @.str11168 }, { ptr, ptr } { ptr @.str11169, ptr @.str11170 }, { ptr, ptr } { ptr @.str11171, ptr @.str11172 }, { ptr, ptr } { ptr @.str11173, ptr @.str11174 }, { ptr, ptr } { ptr @.str11175, ptr @.str11176 }, { ptr, ptr } { ptr @.str11177, ptr @.str11178 }, { ptr, ptr } { ptr @.str11179, ptr @.str11180 }, { ptr, ptr } { ptr @.str11181, ptr @.str11182 }, { ptr, ptr } { ptr @.str11183, ptr @.str11184 }, { ptr, ptr } { ptr @.str11185, ptr @.str11186 }, { ptr, ptr } { ptr @.str11187, ptr @.str11188 }, { ptr, ptr } { ptr @.str11189, ptr @.str11190 }, { ptr, ptr } { ptr @.str11191, ptr @.str11192 }, { ptr, ptr } { ptr @.str11193, ptr @.str11194 }, { ptr, ptr } { ptr @.str11195, ptr @.str11196 }, { ptr, ptr } { ptr @.str11197, ptr @.str11198 }, { ptr, ptr } { ptr @.str11199, ptr @.str11200 }, { ptr, ptr } { ptr @.str11201, ptr @.str11202 }, { ptr, ptr } { ptr @.str11203, ptr @.str11204 }, { ptr, ptr } { ptr @.str11205, ptr @.str11206 }, { ptr, ptr } { ptr @.str11207, ptr @.str11208 }, { ptr, ptr } { ptr @.str11209, ptr @.str11210 }] +@.str11170 = private unnamed_addr constant [4 x i8] c"png\00" +@.str11171 = private unnamed_addr constant [10 x i8] c"image/png\00" +@.str11172 = private unnamed_addr constant [4 x i8] c"jpg\00" +@.str11173 = private unnamed_addr constant [11 x i8] c"image/jpeg\00" +@.str11174 = private unnamed_addr constant [5 x i8] c"jpeg\00" +@.str11175 = private unnamed_addr constant [11 x i8] c"image/jpeg\00" +@.str11176 = private unnamed_addr constant [4 x i8] c"gif\00" +@.str11177 = private unnamed_addr constant [10 x i8] c"image/gif\00" +@.str11178 = private unnamed_addr constant [4 x i8] c"bmp\00" +@.str11179 = private unnamed_addr constant [10 x i8] c"image/bmp\00" +@.str11180 = private unnamed_addr constant [5 x i8] c"webp\00" +@.str11181 = private unnamed_addr constant [11 x i8] c"image/webp\00" +@.str11182 = private unnamed_addr constant [4 x i8] c"svg\00" +@.str11183 = private unnamed_addr constant [14 x i8] c"image/svg+xml\00" +@.str11184 = private unnamed_addr constant [4 x i8] c"txt\00" +@.str11185 = private unnamed_addr constant [11 x i8] c"text/plain\00" +@.str11186 = private unnamed_addr constant [3 x i8] c"md\00" +@.str11187 = private unnamed_addr constant [14 x i8] c"text/markdown\00" +@.str11188 = private unnamed_addr constant [4 x i8] c"csv\00" +@.str11189 = private unnamed_addr constant [9 x i8] c"text/csv\00" +@.str11190 = private unnamed_addr constant [5 x i8] c"html\00" +@.str11191 = private unnamed_addr constant [10 x i8] c"text/html\00" +@.str11192 = private unnamed_addr constant [4 x i8] c"htm\00" +@.str11193 = private unnamed_addr constant [10 x i8] c"text/html\00" +@.str11194 = private unnamed_addr constant [4 x i8] c"css\00" +@.str11195 = private unnamed_addr constant [9 x i8] c"text/css\00" +@.str11196 = private unnamed_addr constant [3 x i8] c"js\00" +@.str11197 = private unnamed_addr constant [16 x i8] c"text/javascript\00" +@.str11198 = private unnamed_addr constant [5 x i8] c"json\00" +@.str11199 = private unnamed_addr constant [17 x i8] c"application/json\00" +@.str11200 = private unnamed_addr constant [4 x i8] c"xml\00" +@.str11201 = private unnamed_addr constant [16 x i8] c"application/xml\00" +@.str11202 = private unnamed_addr constant [4 x i8] c"zip\00" +@.str11203 = private unnamed_addr constant [16 x i8] c"application/zip\00" +@.str11204 = private unnamed_addr constant [4 x i8] c"pdf\00" +@.str11205 = private unnamed_addr constant [16 x i8] c"application/pdf\00" +@.str11206 = private unnamed_addr constant [4 x i8] c"wav\00" +@.str11207 = private unnamed_addr constant [10 x i8] c"audio/wav\00" +@.str11208 = private unnamed_addr constant [4 x i8] c"ogg\00" +@.str11209 = private unnamed_addr constant [10 x i8] c"audio/ogg\00" +@.str11210 = private unnamed_addr constant [4 x i8] c"mp3\00" +@.str11211 = private unnamed_addr constant [11 x i8] c"audio/mpeg\00" +@.str11212 = private unnamed_addr constant [4 x i8] c"ttf\00" +@.str11213 = private unnamed_addr constant [9 x i8] c"font/ttf\00" +@.str11214 = private unnamed_addr constant [4 x i8] c"otf\00" +@.str11215 = private unnamed_addr constant [9 x i8] c"font/otf\00" +@.str11216 = private unnamed_addr constant [6 x i8] c"ludic\00" +@.str11217 = private unnamed_addr constant [13 x i8] c"text/x-ludic\00" +@.str11218 = private unnamed_addr constant [25 x i8] c"application/octet-stream\00" +@mime_tbl = private unnamed_addr constant [24 x { ptr, ptr }] [{ ptr, ptr } { ptr @.str11170, ptr @.str11171 }, { ptr, ptr } { ptr @.str11172, ptr @.str11173 }, { ptr, ptr } { ptr @.str11174, ptr @.str11175 }, { ptr, ptr } { ptr @.str11176, ptr @.str11177 }, { ptr, ptr } { ptr @.str11178, ptr @.str11179 }, { ptr, ptr } { ptr @.str11180, ptr @.str11181 }, { ptr, ptr } { ptr @.str11182, ptr @.str11183 }, { ptr, ptr } { ptr @.str11184, ptr @.str11185 }, { ptr, ptr } { ptr @.str11186, ptr @.str11187 }, { ptr, ptr } { ptr @.str11188, ptr @.str11189 }, { ptr, ptr } { ptr @.str11190, ptr @.str11191 }, { ptr, ptr } { ptr @.str11192, ptr @.str11193 }, { ptr, ptr } { ptr @.str11194, ptr @.str11195 }, { ptr, ptr } { ptr @.str11196, ptr @.str11197 }, { ptr, ptr } { ptr @.str11198, ptr @.str11199 }, { ptr, ptr } { ptr @.str11200, ptr @.str11201 }, { ptr, ptr } { ptr @.str11202, ptr @.str11203 }, { ptr, ptr } { ptr @.str11204, ptr @.str11205 }, { ptr, ptr } { ptr @.str11206, ptr @.str11207 }, { ptr, ptr } { ptr @.str11208, ptr @.str11209 }, { ptr, ptr } { ptr @.str11210, ptr @.str11211 }, { ptr, ptr } { ptr @.str11212, ptr @.str11213 }, { ptr, ptr } { ptr @.str11214, ptr @.str11215 }, { ptr, ptr } { ptr @.str11216, ptr @.str11217 }] define ptr @lp_mime_of(ptr %path) { entry: %ext = call ptr @lp_path_ext(ptr %path) @@ -12229,7 +12236,7 @@ nx: store i32 %i1, ptr %ip br label %lp def: - %d = call ptr @lp_fs_strdup(ptr @.str11211) + %d = call ptr @lp_fs_strdup(ptr @.str11218) ret ptr %d } define ptr @lp_mime_lc(ptr %s) { @@ -110113,23 +110120,27 @@ entry: %t125 = alloca i32 %t135 = alloca i32 %t144 = alloca i32 - %t167 = alloca i32 - %t196 = alloca i32 - %t197 = alloca i32 - %t216 = alloca ptr - %t226 = alloca ptr - %t233 = alloca ptr - %t235 = alloca ptr - %t240 = alloca ptr - %t245 = alloca ptr - %t255 = alloca ptr - %t265 = alloca ptr - %t277 = alloca ptr - %t280 = alloca ptr - %t291 = alloca ptr - %t294 = alloca ptr - %t309 = alloca i32 - %t330 = alloca i32 + %t161 = alloca i32 + %t170 = alloca i32 + %t199 = alloca i32 + %t200 = alloca i32 + %t219 = alloca ptr + %t229 = alloca ptr + %t236 = alloca ptr + %t238 = alloca ptr + %t243 = alloca ptr + %t250 = alloca ptr + %t260 = alloca ptr + %t269 = alloca ptr + %t279 = alloca ptr + %t289 = alloca ptr + %t301 = alloca ptr + %t304 = alloca ptr + %t307 = alloca ptr + %t325 = alloca ptr + %t328 = alloca ptr + %t343 = alloca i32 + %t364 = alloca i32 store i32 %argc, ptr @L_argc store ptr %argv, ptr @L_argv store i32 0, ptr %retval @@ -110464,284 +110475,335 @@ then70: %t160 = call i32 @fn_ir_flush(ptr null) br label %ret ifend72: - %t161 = load i32, ptr @g_g_target_win - %t162 = icmp ne i32 %t161, 0 - br i1 %t162, label %then73, label %ifend75 -then73: - %t163 = load i32, ptr @g_g_windowed - %t164 = icmp ne i32 %t163, 0 - br i1 %t164, label %then76, label %ifend78 -then76: - call void @fn_die(ptr @.str11129) - br label %ifend78 -ifend78: + %t162 = load i32, ptr @g_g_target_win + %t163 = icmp ne i32 %t162, 0 + %t164 = zext i1 %t163 to i32 + store i32 %t164, ptr %t161 + br i1 %t163, label %sc73, label %scend74 +sc73: %t165 = load i32, ptr @g_g_uses_http %t166 = icmp ne i32 %t165, 0 - br i1 %t166, label %then79, label %ifend81 -then79: - call void @fn_die(ptr @.str11130) - br label %ifend81 -ifend81: - br label %ifend75 -ifend75: - %t168 = load i32, ptr %t6 + %t167 = zext i1 %t166 to i32 + store i32 %t167, ptr %t161 + br label %scend74 +scend74: + %t168 = load i32, ptr %t161 %t169 = icmp ne i32 %t168, 0 - %t170 = zext i1 %t169 to i32 - store i32 %t170, ptr %t167 - br i1 %t169, label %sc82, label %scend83 -sc82: - %t171 = load ptr, ptr %t1 - %t172 = icmp eq ptr %t171, null + br i1 %t169, label %then75, label %ifend77 +then75: + call void @fn_die(ptr @.str11129) + br label %ifend77 +ifend77: + %t171 = load i32, ptr %t6 + %t172 = icmp ne i32 %t171, 0 %t173 = zext i1 %t172 to i32 - %t174 = icmp ne i32 %t173, 0 - %t175 = zext i1 %t174 to i32 - store i32 %t175, ptr %t167 - br label %scend83 -scend83: - %t176 = load i32, ptr %t167 + store i32 %t173, ptr %t170 + br i1 %t172, label %sc78, label %scend79 +sc78: + %t174 = load ptr, ptr %t1 + %t175 = icmp eq ptr %t174, null + %t176 = zext i1 %t175 to i32 %t177 = icmp ne i32 %t176, 0 - br i1 %t177, label %then84, label %ifend86 -then84: - %t178 = call i32 @fn_host_is_windows() - %t179 = icmp ne i32 %t178, 0 - br i1 %t179, label %then87, label %else88 -then87: - %t180 = call ptr @fn_getenv_or(ptr @.str11131, ptr @.str11132) - %t181 = call ptr @fn_fwd_slashes(ptr %t180) - %t182 = call ptr @lp_str_concat(ptr %t181, ptr @.str11133) - %t183 = load ptr, ptr %t0 - %t184 = call ptr @fn_base_name(ptr %t183) - %t185 = call ptr @fn_strip_ludic(ptr %t184) - %t186 = call ptr @lp_str_concat(ptr %t182, ptr %t185) - store ptr %t186, ptr %t1 - br label %ifend89 -else88: - %t187 = load ptr, ptr %t0 - %t188 = call ptr @fn_base_name(ptr %t187) - %t189 = call ptr @fn_strip_ludic(ptr %t188) - %t190 = call ptr @lp_str_concat(ptr @.str11134, ptr %t189) - store ptr %t190, ptr %t1 - br label %ifend89 -ifend89: - br label %ifend86 -ifend86: - %t191 = load i32, ptr @g_g_target_win - %t192 = icmp ne i32 %t191, 0 - br i1 %t192, label %then90, label %ifend92 -then90: - %t193 = load ptr, ptr %t1 - %t194 = call i64 @strlen(ptr %t193) - %t195 = trunc i64 %t194 to i32 - store i32 %t195, ptr %t196 - store i32 0, ptr %t197 - %t198 = load i32, ptr %t196 - %t199 = icmp sge i32 %t198, 4 - %t200 = zext i1 %t199 to i32 - %t201 = icmp ne i32 %t200, 0 - br i1 %t201, label %then93, label %ifend95 -then93: - %t202 = load ptr, ptr %t1 - %t203 = load i32, ptr %t196 - %t204 = sub i32 %t203, 4 - %t205 = load i32, ptr %t196 - %t206 = call ptr @lp_str_slice(ptr %t202, i32 %t204, i32 %t205) - %t207 = call i32 @lp_str_eq(ptr %t206, ptr @.str11135) - store i32 %t207, ptr %t197 - br label %ifend95 -ifend95: - %t208 = load i32, ptr %t197 - %t209 = icmp eq i32 %t208, 0 - %t210 = zext i1 %t209 to i32 - %t211 = icmp ne i32 %t210, 0 - br i1 %t211, label %then96, label %ifend98 -then96: - %t212 = load ptr, ptr %t1 - %t213 = call ptr @lp_str_concat(ptr %t212, ptr @.str11136) - store ptr %t213, ptr %t1 - br label %ifend98 -ifend98: - br label %ifend92 -ifend92: - %t214 = load ptr, ptr %t1 - %t215 = call ptr @fn_dir_of(ptr %t214) - store ptr %t215, ptr %t216 - %t217 = load ptr, ptr %t216 - %t218 = call i64 @strlen(ptr %t217) - %t219 = trunc i64 %t218 to i32 - %t220 = icmp sgt i32 %t219, 0 - %t221 = zext i1 %t220 to i32 - %t222 = icmp ne i32 %t221, 0 - br i1 %t222, label %then99, label %ifend101 -then99: - %t223 = load ptr, ptr %t216 - call void @fn_make_dir(ptr %t223) - br label %ifend101 -ifend101: - %t224 = load ptr, ptr %t1 - %t225 = call ptr @lp_str_concat(ptr %t224, ptr @.str11137) - store ptr %t225, ptr %t226 - %t227 = load ptr, ptr %t226 - %t228 = call i32 @fn_ir_flush(ptr %t227) - %t229 = icmp eq i32 %t228, 0 - %t230 = zext i1 %t229 to i32 - %t231 = icmp ne i32 %t230, 0 - br i1 %t231, label %then102, label %ifend104 -then102: - call void @fn_die(ptr @.str11138) - br label %ifend104 -ifend104: - %t232 = call ptr @fn_find_cc() - store ptr %t232, ptr %t233 - %t234 = call ptr @fn_ludic_home() - store ptr %t234, ptr %t235 - %t236 = load ptr, ptr %t233 - %t237 = call ptr @lp_str_concat(ptr %t236, ptr @.str11139) - %t238 = load ptr, ptr %t226 - %t239 = call ptr @lp_str_concat(ptr %t237, ptr %t238) - store ptr %t239, ptr %t240 - %t241 = load i32, ptr @g_g_windowed - %t242 = icmp ne i32 %t241, 0 - br i1 %t242, label %then105, label %ifend107 -then105: - %t243 = load ptr, ptr %t235 - %t244 = call ptr @fn_join_path(ptr %t243, ptr @.str11140) - store ptr %t244, ptr %t245 - %t246 = load ptr, ptr %t240 - %t247 = call ptr @lp_str_concat(ptr %t246, ptr @.str11141) - %t248 = load ptr, ptr %t245 - %t249 = call ptr @lp_str_concat(ptr %t247, ptr %t248) - %t250 = call ptr @lp_str_concat(ptr %t249, ptr @.str11142) - store ptr %t250, ptr %t240 - %t251 = load i32, ptr @g_g_uses_audio - %t252 = icmp ne i32 %t251, 0 - br i1 %t252, label %then108, label %ifend110 -then108: - %t253 = load ptr, ptr %t235 - %t254 = call ptr @fn_join_path(ptr %t253, ptr @.str11143) - store ptr %t254, ptr %t255 - %t256 = load ptr, ptr %t240 - %t257 = call ptr @lp_str_concat(ptr %t256, ptr @.str11144) - %t258 = load ptr, ptr %t255 - %t259 = call ptr @lp_str_concat(ptr %t257, ptr %t258) - %t260 = call ptr @lp_str_concat(ptr %t259, ptr @.str11145) - store ptr %t260, ptr %t240 - br label %ifend110 -ifend110: - br label %ifend107 -ifend107: - %t261 = load i32, ptr @g_g_uses_http - %t262 = icmp ne i32 %t261, 0 - br i1 %t262, label %then111, label %ifend113 -then111: - %t263 = load ptr, ptr %t235 - %t264 = call ptr @fn_join_path(ptr %t263, ptr @.str11146) - store ptr %t264, ptr %t265 - %t266 = load ptr, ptr %t240 - %t267 = call ptr @lp_str_concat(ptr %t266, ptr @.str11147) - %t268 = load ptr, ptr %t265 - %t269 = call ptr @lp_str_concat(ptr %t267, ptr %t268) - %t270 = call ptr @lp_str_concat(ptr %t269, ptr @.str11148) - store ptr %t270, ptr %t240 - br label %ifend113 -ifend113: - %t271 = load i32, ptr @g_g_uses_gl - %t272 = icmp ne i32 %t271, 0 - br i1 %t272, label %then114, label %ifend116 -then114: - %t273 = load i32, ptr @g_g_target_win - %t274 = icmp ne i32 %t273, 0 - br i1 %t274, label %then117, label %else118 -then117: - %t275 = load ptr, ptr %t235 - %t276 = call ptr @fn_join_path(ptr %t275, ptr @.str11149) - store ptr %t276, ptr %t277 - %t278 = load ptr, ptr %t235 - %t279 = call ptr @fn_join_path(ptr %t278, ptr @.str11150) - store ptr %t279, ptr %t280 - %t281 = load ptr, ptr %t240 - %t282 = call ptr @lp_str_concat(ptr %t281, ptr @.str11151) - %t283 = load ptr, ptr %t277 - %t284 = call ptr @lp_str_concat(ptr %t282, ptr %t283) - %t285 = call ptr @lp_str_concat(ptr %t284, ptr @.str11152) - %t286 = load ptr, ptr %t280 - %t287 = call ptr @lp_str_concat(ptr %t285, ptr %t286) - %t288 = call ptr @lp_str_concat(ptr %t287, ptr @.str11153) - store ptr %t288, ptr %t240 - br label %ifend119 -else118: - %t289 = load ptr, ptr %t235 - %t290 = call ptr @fn_join_path(ptr %t289, ptr @.str11154) - store ptr %t290, ptr %t291 - %t292 = load ptr, ptr %t235 - %t293 = call ptr @fn_join_path(ptr %t292, ptr @.str11155) - store ptr %t293, ptr %t294 - %t295 = load ptr, ptr %t240 - %t296 = call ptr @lp_str_concat(ptr %t295, ptr @.str11156) - %t297 = load ptr, ptr %t291 - %t298 = call ptr @lp_str_concat(ptr %t296, ptr %t297) - %t299 = call ptr @lp_str_concat(ptr %t298, ptr @.str11157) - %t300 = load ptr, ptr %t294 - %t301 = call ptr @lp_str_concat(ptr %t299, ptr %t300) - %t302 = call ptr @lp_str_concat(ptr %t301, ptr @.str11158) - store ptr %t302, ptr %t240 - br label %ifend119 -ifend119: - br label %ifend116 -ifend116: - %t303 = load ptr, ptr %t240 - %t304 = call ptr @lp_str_concat(ptr %t303, ptr @.str11159) - %t305 = load ptr, ptr %t1 - %t306 = call ptr @lp_str_concat(ptr %t304, ptr %t305) - store ptr %t306, ptr %t240 - %t307 = load ptr, ptr %t240 - %t308 = call i32 @system(ptr %t307) - store i32 %t308, ptr %t309 - %t310 = load i32, ptr %t5 - %t311 = icmp eq i32 %t310, 0 - %t312 = zext i1 %t311 to i32 - %t313 = icmp ne i32 %t312, 0 - br i1 %t313, label %then120, label %ifend122 -then120: - %t314 = load ptr, ptr %t226 - call void @fn_remove_file(ptr %t314) - br label %ifend122 -ifend122: - %t315 = load i32, ptr %t309 - %t316 = icmp ne i32 %t315, 0 - %t317 = zext i1 %t316 to i32 - %t318 = icmp ne i32 %t317, 0 - br i1 %t318, label %then123, label %ifend125 -then123: - call void @fn_die(ptr @.str11160) - br label %ifend125 -ifend125: - %t319 = load i32, ptr %t6 - %t320 = icmp ne i32 %t319, 0 - br i1 %t320, label %then126, label %ifend128 -then126: - %t321 = call i32 @fn_host_is_windows() - %t322 = icmp ne i32 %t321, 0 - br i1 %t322, label %then129, label %ifend131 -then129: - %t323 = load ptr, ptr %t1 - %t324 = call ptr @fn_win_path(ptr %t323) - %t325 = call ptr @lp_str_concat(ptr @.str11161, ptr %t324) - %t326 = call ptr @lp_str_concat(ptr %t325, ptr @.str11162) - %t327 = call i32 @system(ptr %t326) - call void @exit(i32 %t327) + %t178 = zext i1 %t177 to i32 + store i32 %t178, ptr %t170 + br label %scend79 +scend79: + %t179 = load i32, ptr %t170 + %t180 = icmp ne i32 %t179, 0 + br i1 %t180, label %then80, label %ifend82 +then80: + %t181 = call i32 @fn_host_is_windows() + %t182 = icmp ne i32 %t181, 0 + br i1 %t182, label %then83, label %else84 +then83: + %t183 = call ptr @fn_getenv_or(ptr @.str11130, ptr @.str11131) + %t184 = call ptr @fn_fwd_slashes(ptr %t183) + %t185 = call ptr @lp_str_concat(ptr %t184, ptr @.str11132) + %t186 = load ptr, ptr %t0 + %t187 = call ptr @fn_base_name(ptr %t186) + %t188 = call ptr @fn_strip_ludic(ptr %t187) + %t189 = call ptr @lp_str_concat(ptr %t185, ptr %t188) + store ptr %t189, ptr %t1 + br label %ifend85 +else84: + %t190 = load ptr, ptr %t0 + %t191 = call ptr @fn_base_name(ptr %t190) + %t192 = call ptr @fn_strip_ludic(ptr %t191) + %t193 = call ptr @lp_str_concat(ptr @.str11133, ptr %t192) + store ptr %t193, ptr %t1 + br label %ifend85 +ifend85: + br label %ifend82 +ifend82: + %t194 = load i32, ptr @g_g_target_win + %t195 = icmp ne i32 %t194, 0 + br i1 %t195, label %then86, label %ifend88 +then86: + %t196 = load ptr, ptr %t1 + %t197 = call i64 @strlen(ptr %t196) + %t198 = trunc i64 %t197 to i32 + store i32 %t198, ptr %t199 + store i32 0, ptr %t200 + %t201 = load i32, ptr %t199 + %t202 = icmp sge i32 %t201, 4 + %t203 = zext i1 %t202 to i32 + %t204 = icmp ne i32 %t203, 0 + br i1 %t204, label %then89, label %ifend91 +then89: + %t205 = load ptr, ptr %t1 + %t206 = load i32, ptr %t199 + %t207 = sub i32 %t206, 4 + %t208 = load i32, ptr %t199 + %t209 = call ptr @lp_str_slice(ptr %t205, i32 %t207, i32 %t208) + %t210 = call i32 @lp_str_eq(ptr %t209, ptr @.str11134) + store i32 %t210, ptr %t200 + br label %ifend91 +ifend91: + %t211 = load i32, ptr %t200 + %t212 = icmp eq i32 %t211, 0 + %t213 = zext i1 %t212 to i32 + %t214 = icmp ne i32 %t213, 0 + br i1 %t214, label %then92, label %ifend94 +then92: + %t215 = load ptr, ptr %t1 + %t216 = call ptr @lp_str_concat(ptr %t215, ptr @.str11135) + store ptr %t216, ptr %t1 + br label %ifend94 +ifend94: + br label %ifend88 +ifend88: + %t217 = load ptr, ptr %t1 + %t218 = call ptr @fn_dir_of(ptr %t217) + store ptr %t218, ptr %t219 + %t220 = load ptr, ptr %t219 + %t221 = call i64 @strlen(ptr %t220) + %t222 = trunc i64 %t221 to i32 + %t223 = icmp sgt i32 %t222, 0 + %t224 = zext i1 %t223 to i32 + %t225 = icmp ne i32 %t224, 0 + br i1 %t225, label %then95, label %ifend97 +then95: + %t226 = load ptr, ptr %t219 + call void @fn_make_dir(ptr %t226) + br label %ifend97 +ifend97: + %t227 = load ptr, ptr %t1 + %t228 = call ptr @lp_str_concat(ptr %t227, ptr @.str11136) + store ptr %t228, ptr %t229 + %t230 = load ptr, ptr %t229 + %t231 = call i32 @fn_ir_flush(ptr %t230) + %t232 = icmp eq i32 %t231, 0 + %t233 = zext i1 %t232 to i32 + %t234 = icmp ne i32 %t233, 0 + br i1 %t234, label %then98, label %ifend100 +then98: + call void @fn_die(ptr @.str11137) + br label %ifend100 +ifend100: + %t235 = call ptr @fn_find_cc() + store ptr %t235, ptr %t236 + %t237 = call ptr @fn_ludic_home() + store ptr %t237, ptr %t238 + %t239 = load ptr, ptr %t236 + %t240 = call ptr @lp_str_concat(ptr %t239, ptr @.str11138) + %t241 = load ptr, ptr %t229 + %t242 = call ptr @lp_str_concat(ptr %t240, ptr %t241) + store ptr %t242, ptr %t243 + %t244 = load i32, ptr @g_g_target_win + %t245 = icmp ne i32 %t244, 0 + br i1 %t245, label %then101, label %else102 +then101: + %t246 = load i32, ptr @g_g_windowed + %t247 = icmp ne i32 %t246, 0 + br i1 %t247, label %then104, label %ifend106 +then104: + %t248 = load ptr, ptr %t238 + %t249 = call ptr @fn_join_path(ptr %t248, ptr @.str11139) + store ptr %t249, ptr %t250 + %t251 = load ptr, ptr %t243 + %t252 = call ptr @lp_str_concat(ptr %t251, ptr @.str11140) + %t253 = load ptr, ptr %t250 + %t254 = call ptr @lp_str_concat(ptr %t252, ptr %t253) + %t255 = call ptr @lp_str_concat(ptr %t254, ptr @.str11141) + store ptr %t255, ptr %t243 + %t256 = load i32, ptr @g_g_uses_audio + %t257 = icmp ne i32 %t256, 0 + br i1 %t257, label %then107, label %ifend109 +then107: + %t258 = load ptr, ptr %t238 + %t259 = call ptr @fn_join_path(ptr %t258, ptr @.str11142) + store ptr %t259, ptr %t260 + %t261 = load ptr, ptr %t243 + %t262 = call ptr @lp_str_concat(ptr %t261, ptr @.str11143) + %t263 = load ptr, ptr %t260 + %t264 = call ptr @lp_str_concat(ptr %t262, ptr %t263) + store ptr %t264, ptr %t243 + br label %ifend109 +ifend109: + br label %ifend106 +ifend106: + br label %ifend103 +else102: + %t265 = load i32, ptr @g_g_windowed + %t266 = icmp ne i32 %t265, 0 + br i1 %t266, label %then110, label %ifend112 +then110: + %t267 = load ptr, ptr %t238 + %t268 = call ptr @fn_join_path(ptr %t267, ptr @.str11144) + store ptr %t268, ptr %t269 + %t270 = load ptr, ptr %t243 + %t271 = call ptr @lp_str_concat(ptr %t270, ptr @.str11145) + %t272 = load ptr, ptr %t269 + %t273 = call ptr @lp_str_concat(ptr %t271, ptr %t272) + %t274 = call ptr @lp_str_concat(ptr %t273, ptr @.str11146) + store ptr %t274, ptr %t243 + %t275 = load i32, ptr @g_g_uses_audio + %t276 = icmp ne i32 %t275, 0 + br i1 %t276, label %then113, label %ifend115 +then113: + %t277 = load ptr, ptr %t238 + %t278 = call ptr @fn_join_path(ptr %t277, ptr @.str11147) + store ptr %t278, ptr %t279 + %t280 = load ptr, ptr %t243 + %t281 = call ptr @lp_str_concat(ptr %t280, ptr @.str11148) + %t282 = load ptr, ptr %t279 + %t283 = call ptr @lp_str_concat(ptr %t281, ptr %t282) + %t284 = call ptr @lp_str_concat(ptr %t283, ptr @.str11149) + store ptr %t284, ptr %t243 + br label %ifend115 +ifend115: + br label %ifend112 +ifend112: + br label %ifend103 +ifend103: + %t285 = load i32, ptr @g_g_uses_http + %t286 = icmp ne i32 %t285, 0 + br i1 %t286, label %then116, label %ifend118 +then116: + %t287 = load ptr, ptr %t238 + %t288 = call ptr @fn_join_path(ptr %t287, ptr @.str11150) + store ptr %t288, ptr %t289 + %t290 = load ptr, ptr %t243 + %t291 = call ptr @lp_str_concat(ptr %t290, ptr @.str11151) + %t292 = load ptr, ptr %t289 + %t293 = call ptr @lp_str_concat(ptr %t291, ptr %t292) + %t294 = call ptr @lp_str_concat(ptr %t293, ptr @.str11152) + store ptr %t294, ptr %t243 + br label %ifend118 +ifend118: + %t295 = load i32, ptr @g_g_uses_gl + %t296 = icmp ne i32 %t295, 0 + br i1 %t296, label %then119, label %ifend121 +then119: + %t297 = load i32, ptr @g_g_target_win + %t298 = icmp ne i32 %t297, 0 + br i1 %t298, label %then122, label %else123 +then122: + %t299 = load ptr, ptr %t238 + %t300 = call ptr @fn_join_path(ptr %t299, ptr @.str11153) + store ptr %t300, ptr %t301 + %t302 = load ptr, ptr %t238 + %t303 = call ptr @fn_join_path(ptr %t302, ptr @.str11154) + store ptr %t303, ptr %t304 + %t305 = load ptr, ptr %t238 + %t306 = call ptr @fn_join_path(ptr %t305, ptr @.str11155) + store ptr %t306, ptr %t307 + %t308 = load i32, ptr @g_g_windowed + %t309 = icmp ne i32 %t308, 0 + br i1 %t309, label %then125, label %ifend127 +then125: + %t310 = load ptr, ptr %t238 + %t311 = call ptr @fn_join_path(ptr %t310, ptr @.str11156) + store ptr %t311, ptr %t307 + br label %ifend127 +ifend127: + %t312 = load ptr, ptr %t243 + %t313 = call ptr @lp_str_concat(ptr %t312, ptr @.str11157) + %t314 = load ptr, ptr %t301 + %t315 = call ptr @lp_str_concat(ptr %t313, ptr %t314) + %t316 = call ptr @lp_str_concat(ptr %t315, ptr @.str11158) + %t317 = load ptr, ptr %t304 + %t318 = call ptr @lp_str_concat(ptr %t316, ptr %t317) + %t319 = call ptr @lp_str_concat(ptr %t318, ptr @.str11159) + %t320 = load ptr, ptr %t307 + %t321 = call ptr @lp_str_concat(ptr %t319, ptr %t320) + %t322 = call ptr @lp_str_concat(ptr %t321, ptr @.str11160) + store ptr %t322, ptr %t243 + br label %ifend124 +else123: + %t323 = load ptr, ptr %t238 + %t324 = call ptr @fn_join_path(ptr %t323, ptr @.str11161) + store ptr %t324, ptr %t325 + %t326 = load ptr, ptr %t238 + %t327 = call ptr @fn_join_path(ptr %t326, ptr @.str11162) + store ptr %t327, ptr %t328 + %t329 = load ptr, ptr %t243 + %t330 = call ptr @lp_str_concat(ptr %t329, ptr @.str11163) + %t331 = load ptr, ptr %t325 + %t332 = call ptr @lp_str_concat(ptr %t330, ptr %t331) + %t333 = call ptr @lp_str_concat(ptr %t332, ptr @.str11164) + %t334 = load ptr, ptr %t328 + %t335 = call ptr @lp_str_concat(ptr %t333, ptr %t334) + %t336 = call ptr @lp_str_concat(ptr %t335, ptr @.str11165) + store ptr %t336, ptr %t243 + br label %ifend124 +ifend124: + br label %ifend121 +ifend121: + %t337 = load ptr, ptr %t243 + %t338 = call ptr @lp_str_concat(ptr %t337, ptr @.str11166) + %t339 = load ptr, ptr %t1 + %t340 = call ptr @lp_str_concat(ptr %t338, ptr %t339) + store ptr %t340, ptr %t243 + %t341 = load ptr, ptr %t243 + %t342 = call i32 @system(ptr %t341) + store i32 %t342, ptr %t343 + %t344 = load i32, ptr %t5 + %t345 = icmp eq i32 %t344, 0 + %t346 = zext i1 %t345 to i32 + %t347 = icmp ne i32 %t346, 0 + br i1 %t347, label %then128, label %ifend130 +then128: + %t348 = load ptr, ptr %t229 + call void @fn_remove_file(ptr %t348) + br label %ifend130 +ifend130: + %t349 = load i32, ptr %t343 + %t350 = icmp ne i32 %t349, 0 + %t351 = zext i1 %t350 to i32 + %t352 = icmp ne i32 %t351, 0 + br i1 %t352, label %then131, label %ifend133 +then131: + call void @fn_die(ptr @.str11167) + br label %ifend133 +ifend133: + %t353 = load i32, ptr %t6 + %t354 = icmp ne i32 %t353, 0 + br i1 %t354, label %then134, label %ifend136 +then134: + %t355 = call i32 @fn_host_is_windows() + %t356 = icmp ne i32 %t355, 0 + br i1 %t356, label %then137, label %ifend139 +then137: + %t357 = load ptr, ptr %t1 + %t358 = call ptr @fn_win_path(ptr %t357) + %t359 = call ptr @lp_str_concat(ptr @.str11168, ptr %t358) + %t360 = call ptr @lp_str_concat(ptr %t359, ptr @.str11169) + %t361 = call i32 @system(ptr %t360) + call void @exit(i32 %t361) unreachable -ifend131: - %t328 = load ptr, ptr %t1 - %t329 = call i32 @system(ptr %t328) - store i32 %t329, ptr %t330 - %t331 = load i32, ptr %t330 - %t332 = lshr i32 %t331, 8 - %t333 = and i32 %t332, 255 - call void @exit(i32 %t333) +ifend139: + %t362 = load ptr, ptr %t1 + %t363 = call i32 @system(ptr %t362) + store i32 %t363, ptr %t364 + %t365 = load i32, ptr %t364 + %t366 = lshr i32 %t365, 8 + %t367 = and i32 %t366, 255 + call void @exit(i32 %t367) unreachable -ifend128: +ifend136: br label %ret ret: - %t334 = load i32, ptr %retval - ret i32 %t334 + %t368 = load i32, ptr %retval + ret i32 %t368 } diff --git a/selfhost/main.ludic b/selfhost/main.ludic index 9546fe70..31dfa2af 100644 --- a/selfhost/main.ludic +++ b/selfhost/main.ludic @@ -199,13 +199,9 @@ entry { if emit_ir { ir_flush(out); return } if not has_target { ir_flush(null); return } - # The Windows runtime is the libc surface so far: a window, audio, HTTP and GL - # are macOS platform layers with no Windows counterpart yet, and linking one - # would fail on a hundred Objective-C symbols rather than say so. - if g_target_win { - if g_windowed { die("ludicc: a windowed build is not available on Windows yet; build with --headless\n") } - if g_uses_http { die("ludicc: Http.* is not available on Windows yet\n") } - } + # Http.* is still a macOS platform layer (NSURLConnection) with no Windows + # counterpart; linking it would fail on Objective-C symbols rather than say so. + if g_target_win and g_uses_http { die("ludicc: Http.* is not available on Windows yet\n") } # a run with no explicit -o lands in a temp file if run and (out == null) { @@ -236,7 +232,19 @@ entry { # substitutes the host's and warns about it on every single build. There is # nothing to act on — the host default is exactly what we want — so it is off. var cmd = `{cc} -O2 -Wno-override-module {ll}` - if g_windowed { + if g_target_win { + # The Windows window: win32.ll (user32, gdi32 for the software present, xinput + # for pads), and audio_win.ll when the program uses Audio.*, as on macOS. + if g_windowed { + let win32 = join_path(home, "runtime/native/win32.ll") + cmd = `{cmd} {win32} -luser32 -lgdi32 -lxinput` + if g_uses_audio { + let audiow = join_path(home, "runtime/native/audio_win.ll") + cmd = `{cmd} {audiow}` + } + } + } + else { if g_windowed { let cocoa = join_path(home, "runtime/native/cocoa.ll") # GameController holds no symbol cocoa.ll references directly (its classes are # reached by name through objc_getClass), so a plain -framework link gets @@ -250,7 +258,7 @@ entry { let audio = join_path(home, "runtime/native/audio.ll") cmd = `{cmd} {audio} -Wl,-needed_framework,AVFoundation` } - } + } } # Http.* (#6) links the native transport and Foundation (NSURLConnection etc., # reached by name). Works headless too, so it is outside the windowed block — # macOS-only for now, which is where the toolchain runs. @@ -261,12 +269,15 @@ entry { # Gl.* links the OpenGL backend: gl.ll (offscreen contexts, float helpers), the # generated per-entry-point ABI thunks, and OpenGL.framework. Windowed or not. # On Windows the same pair is gl_win.ll (a WGL context) and gl_thunks_win.ll (the - # thunks through a table wglGetProcAddress fills), against opengl32/gdi32/user32. + # thunks through a table wglGetProcAddress fills), against opengl32/gdi32/user32, + # with the window half from win32_gl.ll - or, headless, gl_win_nowin.ll's stubs. if g_uses_gl { if g_target_win { let glw = join_path(home, "runtime/native/gl_win.ll") let gltw = join_path(home, "runtime/native/gl_thunks_win.ll") - cmd = `{cmd} {glw} {gltw} -luser32 -lgdi32 -lopengl32` + var glhalf = join_path(home, "runtime/native/gl_win_nowin.ll") + if g_windowed { glhalf = join_path(home, "runtime/native/win32_gl.ll") } + cmd = `{cmd} {glw} {gltw} {glhalf} -luser32 -lgdi32 -lopengl32` } else { let gl = join_path(home, "runtime/native/gl.ll") let glt = join_path(home, "runtime/native/gl_thunks.ll")