feat(windows): Http.* over WinHTTP, and the splash and window icon through WIC
http_win.ll implements http.ll's hs_* contract over WinHTTP: the request is a record built on the game thread, the whole exchange runs on a worker thread, TLS uses the system's certificate checks, and headers are answered from the kept request handle. ludicc links it with winhttp instead of refusing Http.* on Windows. win32.ll decodes the splash and App.set_icon images with WIC (ole32): the splash is a topmost borderless window at the artwork's size in points times the display scale, composited over its background colour; the icon becomes an HICON set on the window now or when win_open makes one. Verified on the PC: examples/library/http.ludic prints its expected output, a real GET to https://git.workshopsoft.io/ returns 200 with its body and Content-Type, and the bundled Maroon Lake shows its splash centred at launch and its icon in the title bar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
55b0e2ebf6
commit
40d78cbd92
4 changed files with 988 additions and 12 deletions
|
|
@ -34,8 +34,12 @@ type: feat
|
|||
- **`ludic bundle` on Windows** — `build/<name>/` holding a GUI-subsystem `<name>.exe`, the
|
||||
`game.lpak` and `packs.index`; an `.ico` beside `app icon` is compiled with `llvm-rc` and
|
||||
linked in. `ludicc` gains `--gui` (no console behind the window) and `--link <file>`.
|
||||
- **Not yet** — 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.
|
||||
- **`Http.*` on Windows** — `http_win.ll` is `http.ll`'s contract over WinHTTP: a request
|
||||
built on the game thread, the exchange on a worker thread, TLS with the system's
|
||||
certificate checks, and response headers answered from the kept request handle.
|
||||
- **The splash and the window icon** — decoded by WIC from the bytes in the pack. The splash
|
||||
is a topmost borderless window at the artwork's own size in points times the display's
|
||||
scale; `App.set_icon` sets the title bar and taskbar icon of a build with no icon resource.
|
||||
- **`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
|
||||
|
|
|
|||
534
runtime/native/http_win.ll
Normal file
534
runtime/native/http_win.ll
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
; ============================================================================
|
||||
; http_win.ll — the Windows HTTP transport, written in LLVM IR.
|
||||
;
|
||||
; http.ll's contract over WinHTTP (winhttp.dll). ludicc links it with winhttp
|
||||
; when a Windows program uses Http.*. TLS is the system's, with certificate
|
||||
; verification: an https:// URL just works, as on macOS.
|
||||
;
|
||||
; The shape is http.ll's. A request is built on the game thread as a plain record
|
||||
; (method, URL, header lines, body); hs_send hands one slot to a worker thread,
|
||||
; which runs the whole blocking exchange and publishes the status, a malloc'd
|
||||
; NUL-terminated copy of the body, and the WinHTTP request handle, kept open so
|
||||
; header queries can be answered later. The done flag is written last, atomically,
|
||||
; so what the poller reads is what the worker finished writing.
|
||||
;
|
||||
; hs_req_new(method, url) -> req hs_req_header(req, name, val)
|
||||
; hs_req_body(req, bytes, len) hs_send(slot, req)
|
||||
; hs_done(slot) -> bool hs_status(slot) -> int
|
||||
; hs_body(slot) -> ptr hs_blen(slot) -> int
|
||||
; hs_header(slot, name) -> cstr hs_free(slot)
|
||||
;
|
||||
; WinHTTP speaks UTF-16; every string crosses in UTF-8 and is converted at the call.
|
||||
; A request record: method 0, url 8, header lines 16 ("Name: value\r\n"...), body 24,
|
||||
; body length 32.
|
||||
; ============================================================================
|
||||
|
||||
declare ptr @malloc(i64)
|
||||
declare ptr @realloc(ptr, i64)
|
||||
declare void @free(ptr)
|
||||
declare i64 @strlen(ptr)
|
||||
declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
|
||||
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
|
||||
declare i32 @MultiByteToWideChar(i32, i32, ptr, i32, ptr, i32)
|
||||
declare i32 @WideCharToMultiByte(i32, i32, ptr, i32, ptr, i32, ptr, ptr)
|
||||
declare ptr @CreateThread(ptr, i64, ptr, ptr, i32, ptr)
|
||||
declare i32 @CloseHandle(ptr)
|
||||
declare ptr @WinHttpOpen(ptr, i32, ptr, ptr, i32)
|
||||
declare i32 @WinHttpCrackUrl(ptr, i32, i32, ptr)
|
||||
declare ptr @WinHttpConnect(ptr, ptr, i16, i32)
|
||||
declare ptr @WinHttpOpenRequest(ptr, ptr, ptr, ptr, ptr, ptr, i32)
|
||||
declare i32 @WinHttpAddRequestHeaders(ptr, ptr, i32, i32)
|
||||
declare i32 @WinHttpSendRequest(ptr, ptr, i32, ptr, i32, i32, i64)
|
||||
declare i32 @WinHttpReceiveResponse(ptr, ptr)
|
||||
declare i32 @WinHttpQueryHeaders(ptr, i32, ptr, ptr, ptr, ptr)
|
||||
declare i32 @WinHttpQueryDataAvailable(ptr, ptr)
|
||||
declare i32 @WinHttpReadData(ptr, ptr, i32, ptr)
|
||||
declare i32 @WinHttpCloseHandle(ptr)
|
||||
|
||||
@.h_agent = private unnamed_addr constant [6 x i16] [i16 76, i16 117, i16 100, i16 105, i16 99, i16 0] ; L"Ludic"
|
||||
@.h_empty = private unnamed_addr constant [1 x i8] c"\00"
|
||||
@.h_colon = private unnamed_addr constant [3 x i8] c": \00"
|
||||
@.h_crlf = private unnamed_addr constant [3 x i8] c"\0D\0A\00"
|
||||
|
||||
; HTTP_SLOTS = 8
|
||||
@H_req = internal global [8 x ptr] zeroinitializer
|
||||
@H_sess = internal global [8 x ptr] zeroinitializer
|
||||
@H_conn = internal global [8 x ptr] zeroinitializer
|
||||
@H_hreq = internal global [8 x ptr] zeroinitializer
|
||||
@H_body = internal global [8 x ptr] zeroinitializer
|
||||
@H_blen = internal global [8 x i32] zeroinitializer
|
||||
@H_status = internal global [8 x i32] zeroinitializer
|
||||
@H_done = internal global [8 x i32] zeroinitializer
|
||||
@H_hdr = internal global [8 x ptr] zeroinitializer
|
||||
|
||||
; ---- strings -----------------------------------------------------------------
|
||||
|
||||
define internal ptr @h_dup(ptr %s) {
|
||||
entry:
|
||||
%n = call i64 @strlen(ptr %s)
|
||||
%n1 = add i64 %n, 1
|
||||
%m = call ptr @malloc(i64 %n1)
|
||||
call void @llvm.memcpy.p0.p0.i64(ptr %m, ptr %s, i64 %n1, i1 false)
|
||||
ret ptr %m
|
||||
}
|
||||
|
||||
; a fresh NUL-terminated UTF-16 copy of a UTF-8 string (CP_UTF8 = 65001)
|
||||
define internal ptr @h_wide(ptr %s) {
|
||||
entry:
|
||||
%n = call i32 @MultiByteToWideChar(i32 65001, i32 0, ptr %s, i32 -1, ptr null, i32 0)
|
||||
%bad = icmp sle i32 %n, 0
|
||||
br i1 %bad, label %empty, label %conv
|
||||
empty:
|
||||
%e = call ptr @malloc(i64 2)
|
||||
store i16 0, ptr %e
|
||||
ret ptr %e
|
||||
conv:
|
||||
%n64 = sext i32 %n to i64
|
||||
%bytes = mul i64 %n64, 2
|
||||
%w = call ptr @malloc(i64 %bytes)
|
||||
%r = call i32 @MultiByteToWideChar(i32 65001, i32 0, ptr %s, i32 -1, ptr %w, i32 %n)
|
||||
ret ptr %w
|
||||
}
|
||||
|
||||
; `a` (or nothing, when null) followed by `b`, in a buffer that replaces `a`
|
||||
define internal ptr @h_append(ptr %a, ptr %b) {
|
||||
entry:
|
||||
%none = icmp eq ptr %a, null
|
||||
br i1 %none, label %zero, label %len
|
||||
zero:
|
||||
br label %grow
|
||||
len:
|
||||
%la0 = call i64 @strlen(ptr %a)
|
||||
br label %grow
|
||||
grow:
|
||||
%la = phi i64 [ 0, %zero ], [ %la0, %len ]
|
||||
%lb = call i64 @strlen(ptr %b)
|
||||
%sum = add i64 %la, %lb
|
||||
%cap = add i64 %sum, 1
|
||||
%m = call ptr @realloc(ptr %a, i64 %cap)
|
||||
%dst = getelementptr i8, ptr %m, i64 %la
|
||||
%lb1 = add i64 %lb, 1
|
||||
call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %b, i64 %lb1, i1 false)
|
||||
ret ptr %m
|
||||
}
|
||||
|
||||
define internal void @h_free_if(ptr %p) {
|
||||
entry:
|
||||
%none = icmp eq ptr %p, null
|
||||
br i1 %none, label %out, label %go
|
||||
go:
|
||||
call void @free(ptr %p)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @h_close_if(ptr %h) {
|
||||
entry:
|
||||
%none = icmp eq ptr %h, null
|
||||
br i1 %none, label %out, label %go
|
||||
go:
|
||||
%r = call i32 @WinHttpCloseHandle(ptr %h)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; ---- request construction -------------------------------------------------------
|
||||
|
||||
define ptr @hs_req_new(ptr %method, ptr %url) {
|
||||
entry:
|
||||
%nou = icmp eq ptr %url, null
|
||||
br i1 %nou, label %fail, label %mk
|
||||
mk:
|
||||
%rec = call ptr @malloc(i64 40)
|
||||
call void @llvm.memset.p0.i64(ptr %rec, i8 0, i64 40, i1 false)
|
||||
%m = call ptr @h_dup(ptr %method)
|
||||
store ptr %m, ptr %rec
|
||||
%u = call ptr @h_dup(ptr %url)
|
||||
%up = getelementptr i8, ptr %rec, i64 8
|
||||
store ptr %u, ptr %up
|
||||
ret ptr %rec
|
||||
fail:
|
||||
ret ptr null
|
||||
}
|
||||
|
||||
define void @hs_req_header(ptr %req, ptr %name, ptr %val) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %req, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%hp = getelementptr i8, ptr %req, i64 16
|
||||
%h0 = load ptr, ptr %hp
|
||||
%h1 = call ptr @h_append(ptr %h0, ptr %name)
|
||||
%h2 = call ptr @h_append(ptr %h1, ptr @.h_colon)
|
||||
%h3 = call ptr @h_append(ptr %h2, ptr %val)
|
||||
%h4 = call ptr @h_append(ptr %h3, ptr @.h_crlf)
|
||||
store ptr %h4, ptr %hp
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; the body is copied, as NSData copies it on macOS: the caller may free its buffer
|
||||
define void @hs_req_body(ptr %req, ptr %bytes, i32 %len) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %req, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%bp = getelementptr i8, ptr %req, i64 24
|
||||
%old = load ptr, ptr %bp
|
||||
call void @h_free_if(ptr %old)
|
||||
%l64 = sext i32 %len to i64
|
||||
%cap = add i64 %l64, 1
|
||||
%b = call ptr @malloc(i64 %cap)
|
||||
call void @llvm.memcpy.p0.p0.i64(ptr %b, ptr %bytes, i64 %l64, i1 false)
|
||||
store ptr %b, ptr %bp
|
||||
%lp = getelementptr i8, ptr %req, i64 32
|
||||
store i32 %len, ptr %lp
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; ---- the worker --------------------------------------------------------------------
|
||||
|
||||
; One whole exchange for one slot, on its own thread. arg is the slot index.
|
||||
define internal i32 @h_worker(ptr %arg) {
|
||||
entry:
|
||||
%uc = alloca [104 x i8], align 8 ; URL_COMPONENTS
|
||||
%num = alloca i32, align 4
|
||||
%numlen = alloca i32, align 4
|
||||
%avail = alloca i32, align 4
|
||||
%got = alloca i32, align 4
|
||||
%wurlp = alloca ptr, align 8
|
||||
%hostp = alloca ptr, align 8
|
||||
%verbp = alloca ptr, align 8
|
||||
%whdrp = alloca ptr, align 8
|
||||
%bufp = alloca ptr, align 8
|
||||
%lenp = alloca i64, align 8
|
||||
%capp = alloca i64, align 8
|
||||
store ptr null, ptr %wurlp
|
||||
store ptr null, ptr %hostp
|
||||
store ptr null, ptr %verbp
|
||||
store ptr null, ptr %whdrp
|
||||
%slot64 = ptrtoint ptr %arg to i64
|
||||
%slot = trunc i64 %slot64 to i32
|
||||
%reqp = getelementptr [8 x ptr], ptr @H_req, i32 0, i32 %slot
|
||||
%req = load ptr, ptr %reqp
|
||||
%murlp = getelementptr i8, ptr %req, i64 8
|
||||
%murl = load ptr, ptr %murlp
|
||||
%wurl = call ptr @h_wide(ptr %murl)
|
||||
store ptr %wurl, ptr %wurlp
|
||||
; WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY: the system's proxy settings, as NSURLConnection uses
|
||||
%sess = call ptr @WinHttpOpen(ptr @.h_agent, i32 4, ptr null, ptr null, i32 0)
|
||||
%sessp = getelementptr [8 x ptr], ptr @H_sess, i32 0, i32 %slot
|
||||
store ptr %sess, ptr %sessp
|
||||
%nosess = icmp eq ptr %sess, null
|
||||
br i1 %nosess, label %done, label %crack
|
||||
crack:
|
||||
; a non-zero length asks WinHTTP for a pointer into the URL instead of a copy
|
||||
call void @llvm.memset.p0.i64(ptr %uc, i8 0, i64 104, i1 false)
|
||||
store i32 104, ptr %uc
|
||||
%sl = getelementptr i8, ptr %uc, i64 16
|
||||
store i32 -1, ptr %sl
|
||||
%hl = getelementptr i8, ptr %uc, i64 32
|
||||
store i32 -1, ptr %hl
|
||||
%pl = getelementptr i8, ptr %uc, i64 80
|
||||
store i32 -1, ptr %pl
|
||||
%xl = getelementptr i8, ptr %uc, i64 96
|
||||
store i32 -1, ptr %xl
|
||||
%ok = call i32 @WinHttpCrackUrl(ptr %wurl, i32 0, i32 0, ptr %uc)
|
||||
%noparse = icmp eq i32 %ok, 0
|
||||
br i1 %noparse, label %done, label %connect
|
||||
connect:
|
||||
%hsp = getelementptr i8, ptr %uc, i64 24
|
||||
%hs = load ptr, ptr %hsp
|
||||
%hlen = load i32, ptr %hl
|
||||
%hlen64 = zext i32 %hlen to i64
|
||||
%hbytes = mul i64 %hlen64, 2
|
||||
%hcap = add i64 %hbytes, 2
|
||||
%host = call ptr @malloc(i64 %hcap)
|
||||
call void @llvm.memcpy.p0.p0.i64(ptr %host, ptr %hs, i64 %hbytes, i1 false)
|
||||
%hend = getelementptr i8, ptr %host, i64 %hbytes
|
||||
store i16 0, ptr %hend
|
||||
store ptr %host, ptr %hostp
|
||||
%portp = getelementptr i8, ptr %uc, i64 36
|
||||
%port = load i16, ptr %portp
|
||||
%conn = call ptr @WinHttpConnect(ptr %sess, ptr %host, i16 %port, i32 0)
|
||||
%connp = getelementptr [8 x ptr], ptr @H_conn, i32 0, i32 %slot
|
||||
store ptr %conn, ptr %connp
|
||||
%noconn = icmp eq ptr %conn, null
|
||||
br i1 %noconn, label %done, label %open
|
||||
open:
|
||||
; the path runs on to the end of the URL, so it carries the query with it
|
||||
%pathp = getelementptr i8, ptr %uc, i64 72
|
||||
%path = load ptr, ptr %pathp
|
||||
%schp = getelementptr i8, ptr %uc, i64 20
|
||||
%sch = load i32, ptr %schp
|
||||
%https = icmp eq i32 %sch, 2 ; INTERNET_SCHEME_HTTPS
|
||||
%flags = select i1 %https, i32 8388608, i32 0 ; WINHTTP_FLAG_SECURE
|
||||
%mp = load ptr, ptr %req
|
||||
%verb = call ptr @h_wide(ptr %mp)
|
||||
store ptr %verb, ptr %verbp
|
||||
%hreq = call ptr @WinHttpOpenRequest(ptr %conn, ptr %verb, ptr %path, ptr null, ptr null, ptr null, i32 %flags)
|
||||
%hreqp = getelementptr [8 x ptr], ptr @H_hreq, i32 0, i32 %slot
|
||||
store ptr %hreq, ptr %hreqp
|
||||
%noreq = icmp eq ptr %hreq, null
|
||||
br i1 %noreq, label %done, label %headers
|
||||
headers:
|
||||
%hdrsp = getelementptr i8, ptr %req, i64 16
|
||||
%hdrs = load ptr, ptr %hdrsp
|
||||
%nohdrs = icmp eq ptr %hdrs, null
|
||||
br i1 %nohdrs, label %send, label %addh
|
||||
addh:
|
||||
%wh = call ptr @h_wide(ptr %hdrs)
|
||||
store ptr %wh, ptr %whdrp
|
||||
; WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE: a header set twice is the last value, as setValue:forHTTPHeaderField:
|
||||
%ah = call i32 @WinHttpAddRequestHeaders(ptr %hreq, ptr %wh, i32 -1, i32 -1610612736)
|
||||
br label %send
|
||||
send:
|
||||
%bodyp = getelementptr i8, ptr %req, i64 24
|
||||
%body = load ptr, ptr %bodyp
|
||||
%blp = getelementptr i8, ptr %req, i64 32
|
||||
%bl = load i32, ptr %blp
|
||||
%sent = call i32 @WinHttpSendRequest(ptr %hreq, ptr null, i32 0, ptr %body, i32 %bl, i32 %bl, i64 0)
|
||||
%nosend = icmp eq i32 %sent, 0
|
||||
br i1 %nosend, label %done, label %receive
|
||||
receive:
|
||||
%rr = call i32 @WinHttpReceiveResponse(ptr %hreq, ptr null)
|
||||
%norecv = icmp eq i32 %rr, 0
|
||||
br i1 %norecv, label %done, label %status
|
||||
status:
|
||||
; WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER
|
||||
store i32 0, ptr %num
|
||||
store i32 4, ptr %numlen
|
||||
%qs = call i32 @WinHttpQueryHeaders(ptr %hreq, i32 536870931, ptr null, ptr %num, ptr %numlen, ptr null)
|
||||
%st = load i32, ptr %num
|
||||
%stp = getelementptr [8 x i32], ptr @H_status, i32 0, i32 %slot
|
||||
store i32 %st, ptr %stp
|
||||
%b0 = call ptr @malloc(i64 4097)
|
||||
store ptr %b0, ptr %bufp
|
||||
store i64 0, ptr %lenp
|
||||
store i64 4096, ptr %capp
|
||||
br label %more
|
||||
more:
|
||||
store i32 0, ptr %avail
|
||||
%qa = call i32 @WinHttpQueryDataAvailable(ptr %hreq, ptr %avail)
|
||||
%av = load i32, ptr %avail
|
||||
%qfail = icmp eq i32 %qa, 0
|
||||
%nothing = icmp eq i32 %av, 0
|
||||
%stop = or i1 %qfail, %nothing
|
||||
br i1 %stop, label %finish, label %room
|
||||
room:
|
||||
%len = load i64, ptr %lenp
|
||||
%cap = load i64, ptr %capp
|
||||
%av64 = zext i32 %av to i64
|
||||
%need = add i64 %len, %av64
|
||||
%small = icmp ugt i64 %need, %cap
|
||||
br i1 %small, label %enlarge, label %read
|
||||
enlarge:
|
||||
%ncap = mul i64 %need, 2
|
||||
store i64 %ncap, ptr %capp
|
||||
%ob = load ptr, ptr %bufp
|
||||
%ncap1 = add i64 %ncap, 1
|
||||
%nb = call ptr @realloc(ptr %ob, i64 %ncap1)
|
||||
store ptr %nb, ptr %bufp
|
||||
br label %read
|
||||
read:
|
||||
%buf = load ptr, ptr %bufp
|
||||
%len2 = load i64, ptr %lenp
|
||||
%dst = getelementptr i8, ptr %buf, i64 %len2
|
||||
store i32 0, ptr %got
|
||||
%rd = call i32 @WinHttpReadData(ptr %hreq, ptr %dst, i32 %av, ptr %got)
|
||||
%g = load i32, ptr %got
|
||||
%g64 = zext i32 %g to i64
|
||||
%len3 = add i64 %len2, %g64
|
||||
store i64 %len3, ptr %lenp
|
||||
%rfail = icmp eq i32 %rd, 0
|
||||
%none = icmp eq i32 %g, 0
|
||||
%end = or i1 %rfail, %none
|
||||
br i1 %end, label %finish, label %more
|
||||
finish:
|
||||
%fb = load ptr, ptr %bufp
|
||||
%fl = load i64, ptr %lenp
|
||||
%term = getelementptr i8, ptr %fb, i64 %fl
|
||||
store i8 0, ptr %term
|
||||
%bodyslot = getelementptr [8 x ptr], ptr @H_body, i32 0, i32 %slot
|
||||
store ptr %fb, ptr %bodyslot
|
||||
%fl32 = trunc i64 %fl to i32
|
||||
%blenslot = getelementptr [8 x i32], ptr @H_blen, i32 0, i32 %slot
|
||||
store i32 %fl32, ptr %blenslot
|
||||
br label %done
|
||||
done:
|
||||
%f1 = load ptr, ptr %wurlp
|
||||
call void @h_free_if(ptr %f1)
|
||||
%f2 = load ptr, ptr %hostp
|
||||
call void @h_free_if(ptr %f2)
|
||||
%f3 = load ptr, ptr %verbp
|
||||
call void @h_free_if(ptr %f3)
|
||||
%f4 = load ptr, ptr %whdrp
|
||||
call void @h_free_if(ptr %f4)
|
||||
%donep = getelementptr [8 x i32], ptr @H_done, i32 0, i32 %slot
|
||||
store atomic i32 1, ptr %donep release, align 4
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
; Start the request on `slot` running on a worker thread of its own.
|
||||
define void @hs_send(i32 %slot, ptr %req) {
|
||||
entry:
|
||||
%donep = getelementptr [8 x i32], ptr @H_done, i32 0, i32 %slot
|
||||
store atomic i32 0, ptr %donep release, align 4
|
||||
%stp = getelementptr [8 x i32], ptr @H_status, i32 0, i32 %slot
|
||||
store i32 0, ptr %stp
|
||||
%bodyp = getelementptr [8 x ptr], ptr @H_body, i32 0, i32 %slot
|
||||
store ptr null, ptr %bodyp
|
||||
%blenp = getelementptr [8 x i32], ptr @H_blen, i32 0, i32 %slot
|
||||
store i32 0, ptr %blenp
|
||||
%reqp = getelementptr [8 x ptr], ptr @H_req, i32 0, i32 %slot
|
||||
store ptr %req, ptr %reqp
|
||||
%nil = icmp eq ptr %req, null
|
||||
br i1 %nil, label %nothing, label %start
|
||||
nothing:
|
||||
; a request that could not be built finishes at once, with status 0, as a failed one does
|
||||
store atomic i32 1, ptr %donep release, align 4
|
||||
ret void
|
||||
start:
|
||||
%arg = inttoptr i32 %slot to ptr
|
||||
%t = call ptr @CreateThread(ptr null, i64 0, ptr @h_worker, ptr %arg, i32 0, ptr null)
|
||||
%not = icmp eq ptr %t, null
|
||||
br i1 %not, label %nothing, label %detach
|
||||
detach:
|
||||
%c = call i32 @CloseHandle(ptr %t)
|
||||
ret void
|
||||
}
|
||||
|
||||
; ---- result accessors ---------------------------------------------------------------
|
||||
|
||||
define i32 @hs_done(i32 %slot) {
|
||||
entry:
|
||||
%p = getelementptr [8 x i32], ptr @H_done, i32 0, i32 %slot
|
||||
%v = load atomic i32, ptr %p acquire, align 4
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
define i32 @hs_status(i32 %slot) {
|
||||
entry:
|
||||
%p = getelementptr [8 x i32], ptr @H_status, i32 0, i32 %slot
|
||||
%v = load i32, ptr %p
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
define ptr @hs_body(i32 %slot) {
|
||||
entry:
|
||||
%p = getelementptr [8 x ptr], ptr @H_body, i32 0, i32 %slot
|
||||
%v = load ptr, ptr %p
|
||||
ret ptr %v
|
||||
}
|
||||
|
||||
define i32 @hs_blen(i32 %slot) {
|
||||
entry:
|
||||
%p = getelementptr [8 x i32], ptr @H_blen, i32 0, i32 %slot
|
||||
%v = load i32, ptr %p
|
||||
ret i32 %v
|
||||
}
|
||||
|
||||
; A response header value as a C string, or "" if absent / no response yet. The string
|
||||
; lives until the slot's next header query or hs_free.
|
||||
define ptr @hs_header(i32 %slot, ptr %name) {
|
||||
entry:
|
||||
%lenp = alloca i32, align 4
|
||||
%hp = getelementptr [8 x ptr], ptr @H_hreq, i32 0, i32 %slot
|
||||
%h = load ptr, ptr %hp
|
||||
%noh = icmp eq ptr %h, null
|
||||
br i1 %noh, label %empty, label %size
|
||||
size:
|
||||
%wn = call ptr @h_wide(ptr %name)
|
||||
; WINHTTP_QUERY_CUSTOM with no buffer answers the size needed, in bytes
|
||||
store i32 0, ptr %lenp
|
||||
%q0 = call i32 @WinHttpQueryHeaders(ptr %h, i32 65535, ptr %wn, ptr null, ptr %lenp, ptr null)
|
||||
%need = load i32, ptr %lenp
|
||||
%absent = icmp sle i32 %need, 0
|
||||
br i1 %absent, label %nothere, label %fetch
|
||||
nothere:
|
||||
call void @free(ptr %wn)
|
||||
br label %empty
|
||||
fetch:
|
||||
%n64 = zext i32 %need to i64
|
||||
%cap = add i64 %n64, 2
|
||||
%wb = call ptr @malloc(i64 %cap)
|
||||
%q1 = call i32 @WinHttpQueryHeaders(ptr %h, i32 65535, ptr %wn, ptr %wb, ptr %lenp, ptr null)
|
||||
call void @free(ptr %wn)
|
||||
%got = load i32, ptr %lenp
|
||||
%g64 = zext i32 %got to i64
|
||||
%endp = getelementptr i8, ptr %wb, i64 %g64
|
||||
store i16 0, ptr %endp
|
||||
%failq = icmp eq i32 %q1, 0
|
||||
br i1 %failq, label %freewb, label %utf8
|
||||
freewb:
|
||||
call void @free(ptr %wb)
|
||||
br label %empty
|
||||
utf8:
|
||||
%n8 = call i32 @WideCharToMultiByte(i32 65001, i32 0, ptr %wb, i32 -1, ptr null, i32 0, ptr null, ptr null)
|
||||
%n8z = icmp sle i32 %n8, 0
|
||||
br i1 %n8z, label %freewb, label %conv
|
||||
conv:
|
||||
%n864 = sext i32 %n8 to i64
|
||||
%out = call ptr @malloc(i64 %n864)
|
||||
%c = call i32 @WideCharToMultiByte(i32 65001, i32 0, ptr %wb, i32 -1, ptr %out, i32 %n8, ptr null, ptr null)
|
||||
call void @free(ptr %wb)
|
||||
%oldp = getelementptr [8 x ptr], ptr @H_hdr, i32 0, i32 %slot
|
||||
%old = load ptr, ptr %oldp
|
||||
call void @h_free_if(ptr %old)
|
||||
store ptr %out, ptr %oldp
|
||||
ret ptr %out
|
||||
empty:
|
||||
ret ptr @.h_empty
|
||||
}
|
||||
|
||||
; Free a slot's owned resources: the body copy, the last header string, the three
|
||||
; WinHTTP handles, and the request record.
|
||||
define void @hs_free(i32 %slot) {
|
||||
entry:
|
||||
%bp = getelementptr [8 x ptr], ptr @H_body, i32 0, i32 %slot
|
||||
%b = load ptr, ptr %bp
|
||||
call void @h_free_if(ptr %b)
|
||||
store ptr null, ptr %bp
|
||||
%hdp = getelementptr [8 x ptr], ptr @H_hdr, i32 0, i32 %slot
|
||||
%hd = load ptr, ptr %hdp
|
||||
call void @h_free_if(ptr %hd)
|
||||
store ptr null, ptr %hdp
|
||||
%rqp = getelementptr [8 x ptr], ptr @H_hreq, i32 0, i32 %slot
|
||||
%rq = load ptr, ptr %rqp
|
||||
call void @h_close_if(ptr %rq)
|
||||
store ptr null, ptr %rqp
|
||||
%cnp = getelementptr [8 x ptr], ptr @H_conn, i32 0, i32 %slot
|
||||
%cn = load ptr, ptr %cnp
|
||||
call void @h_close_if(ptr %cn)
|
||||
store ptr null, ptr %cnp
|
||||
%ssp = getelementptr [8 x ptr], ptr @H_sess, i32 0, i32 %slot
|
||||
%ss = load ptr, ptr %ssp
|
||||
call void @h_close_if(ptr %ss)
|
||||
store ptr null, ptr %ssp
|
||||
%recp = getelementptr [8 x ptr], ptr @H_req, i32 0, i32 %slot
|
||||
%rec = load ptr, ptr %recp
|
||||
%norec = icmp eq ptr %rec, null
|
||||
br i1 %norec, label %out, label %freerec
|
||||
freerec:
|
||||
%m = load ptr, ptr %rec
|
||||
call void @h_free_if(ptr %m)
|
||||
%up = getelementptr i8, ptr %rec, i64 8
|
||||
%u = load ptr, ptr %up
|
||||
call void @h_free_if(ptr %u)
|
||||
%hp = getelementptr i8, ptr %rec, i64 16
|
||||
%h = load ptr, ptr %hp
|
||||
call void @h_free_if(ptr %h)
|
||||
%bdp = getelementptr i8, ptr %rec, i64 24
|
||||
%bd = load ptr, ptr %bdp
|
||||
call void @h_free_if(ptr %bd)
|
||||
call void @free(ptr %rec)
|
||||
store ptr null, ptr %recp
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
|
@ -237,6 +237,7 @@ show:
|
|||
store ptr %hwnd, ptr @W_hwnd
|
||||
%hdc = call ptr @GetDC(ptr %hwnd)
|
||||
store ptr %hdc, ptr @W_hdc
|
||||
call void @w_apply_icon() ; an App.set_icon that ran before there was a window
|
||||
%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
|
||||
|
|
@ -893,19 +894,452 @@ entry:
|
|||
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.
|
||||
; ---- images: a PNG or JPEG decoded by WIC --------------------------------------
|
||||
;
|
||||
; The splash and the window icon arrive as file bytes (out of the pack), and WIC -
|
||||
; the codecs every Windows install carries - turns them into 32-bit BGRA, as NSImage
|
||||
; does on macOS. COM slots, from wincodec.h:
|
||||
; IWICImagingFactory 4 CreateDecoderFromStream 10 CreateFormatConverter 14 CreateStream
|
||||
; IWICStream 16 InitializeFromMemory
|
||||
; IWICBitmapDecoder 13 GetFrame
|
||||
; IWICBitmapFrameDecode 5 GetResolution
|
||||
; IWICFormatConverter 3 GetSize 7 CopyPixels 8 Initialize
|
||||
; and 2 Release on all of them.
|
||||
|
||||
declare i32 @CoInitializeEx(ptr, i32)
|
||||
declare i32 @CoCreateInstance(ptr, ptr, i32, ptr, ptr)
|
||||
declare ptr @CreateDIBSection(ptr, ptr, i32, ptr, ptr, i32)
|
||||
declare ptr @CreateBitmap(i32, i32, i32, i32, ptr)
|
||||
declare ptr @CreateIconIndirect(ptr)
|
||||
declare i32 @DeleteObject(ptr)
|
||||
declare i64 @SendMessageA(ptr, i32, i64, i64)
|
||||
declare i32 @DestroyWindow(ptr)
|
||||
declare i32 @UpdateWindow(ptr)
|
||||
declare ptr @BeginPaint(ptr, ptr)
|
||||
declare i32 @EndPaint(ptr, ptr)
|
||||
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
|
||||
|
||||
; CLSID_WICImagingFactory {cacaf262-9370-4615-a13b-9f5539da4c0a},
|
||||
; IID_IWICImagingFactory {ec5ec8a9-c395-4314-9c77-54d7a935ff70},
|
||||
; GUID_WICPixelFormat32bppBGRA {6fddc324-4e03-4bfe-b185-3d77768dc90f} - in memory order
|
||||
@.wic_clsid = private unnamed_addr constant [16 x i8] c"\62\F2\CA\CA\70\93\15\46\A1\3B\9F\55\39\DA\4C\0A"
|
||||
@.wic_iid = private unnamed_addr constant [16 x i8] c"\A9\C8\5E\EC\95\C3\14\43\9C\77\54\D7\A9\35\FF\70"
|
||||
@.wic_bgra = private unnamed_addr constant [16 x i8] c"\24\C3\DD\6F\03\4E\FE\4B\B1\85\3D\77\76\8D\C9\0F"
|
||||
@.w_splash_class = private unnamed_addr constant [12 x i8] c"LudicSplash\00"
|
||||
@.w_empty = private unnamed_addr constant [1 x i8] c"\00"
|
||||
|
||||
define internal ptr @w_vt(ptr %obj, i64 %slot) {
|
||||
entry:
|
||||
%vt = load ptr, ptr %obj
|
||||
%p = getelementptr ptr, ptr %vt, i64 %slot
|
||||
%f = load ptr, ptr %p
|
||||
ret ptr %f
|
||||
}
|
||||
|
||||
define internal void @w_release(ptr %pp) {
|
||||
entry:
|
||||
%o = load ptr, ptr %pp
|
||||
%none = icmp eq ptr %o, null
|
||||
br i1 %none, label %out, label %go
|
||||
go:
|
||||
%rel = call ptr @w_vt(ptr %o, i64 2)
|
||||
%r = call i32 %rel(ptr %o)
|
||||
store ptr null, ptr %pp
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; bytes -> a malloc'd w x h BGRA buffer (straight alpha), its size, and the image's own
|
||||
; horizontal dpi (72 when it names none); null when WIC cannot read it
|
||||
define internal ptr @w_decode(ptr %bytes, i32 %len, ptr %wout, ptr %hout, ptr %dpiout) {
|
||||
entry:
|
||||
%fp = alloca ptr, align 8
|
||||
%sp = alloca ptr, align 8
|
||||
%dp = alloca ptr, align 8
|
||||
%frp = alloca ptr, align 8
|
||||
%cp = alloca ptr, align 8
|
||||
%wp = alloca i32, align 4
|
||||
%hp = alloca i32, align 4
|
||||
%dx = alloca double, align 8
|
||||
%dy = alloca double, align 8
|
||||
store ptr null, ptr %fp
|
||||
store ptr null, ptr %sp
|
||||
store ptr null, ptr %dp
|
||||
store ptr null, ptr %frp
|
||||
store ptr null, ptr %cp
|
||||
store double 0.000000e+00, ptr %dx
|
||||
%nob = icmp eq ptr %bytes, null
|
||||
%empty = icmp sle i32 %len, 0
|
||||
%skip = or i1 %nob, %empty
|
||||
br i1 %skip, label %fail, label %com
|
||||
com:
|
||||
; COINIT_APARTMENTTHREADED; a thread already initialised answers S_FALSE or RPC_E_CHANGED_MODE, both fine
|
||||
%ci = call i32 @CoInitializeEx(ptr null, i32 2)
|
||||
%hr0 = call i32 @CoCreateInstance(ptr @.wic_clsid, ptr null, i32 1, ptr @.wic_iid, ptr %fp) ; CLSCTX_INPROC_SERVER
|
||||
%b0 = icmp slt i32 %hr0, 0
|
||||
br i1 %b0, label %fail, label %stream
|
||||
stream:
|
||||
%f = load ptr, ptr %fp
|
||||
%cs = call ptr @w_vt(ptr %f, i64 14)
|
||||
%hr1 = call i32 %cs(ptr %f, ptr %sp)
|
||||
%b1 = icmp slt i32 %hr1, 0
|
||||
br i1 %b1, label %fail, label %meminit
|
||||
meminit:
|
||||
%s = load ptr, ptr %sp
|
||||
%im = call ptr @w_vt(ptr %s, i64 16)
|
||||
%hr2 = call i32 %im(ptr %s, ptr %bytes, i32 %len)
|
||||
%b2 = icmp slt i32 %hr2, 0
|
||||
br i1 %b2, label %fail, label %decoder
|
||||
decoder:
|
||||
%cdfs = call ptr @w_vt(ptr %f, i64 4)
|
||||
%hr3 = call i32 %cdfs(ptr %f, ptr %s, ptr null, i32 0, ptr %dp) ; WICDecodeMetadataCacheOnDemand
|
||||
%b3 = icmp slt i32 %hr3, 0
|
||||
br i1 %b3, label %fail, label %frame
|
||||
frame:
|
||||
%d = load ptr, ptr %dp
|
||||
%gf = call ptr @w_vt(ptr %d, i64 13)
|
||||
%hr4 = call i32 %gf(ptr %d, i32 0, ptr %frp)
|
||||
%b4 = icmp slt i32 %hr4, 0
|
||||
br i1 %b4, label %fail, label %resolution
|
||||
resolution:
|
||||
%fr = load ptr, ptr %frp
|
||||
%gr = call ptr @w_vt(ptr %fr, i64 5)
|
||||
%hr5 = call i32 %gr(ptr %fr, ptr %dx, ptr %dy)
|
||||
%conv = call ptr @w_vt(ptr %f, i64 10)
|
||||
%hr6 = call i32 %conv(ptr %f, ptr %cp)
|
||||
%b6 = icmp slt i32 %hr6, 0
|
||||
br i1 %b6, label %fail, label %convert
|
||||
convert:
|
||||
%c = load ptr, ptr %cp
|
||||
%ini = call ptr @w_vt(ptr %c, i64 8)
|
||||
; to 32bpp BGRA, no dither, no palette, alpha threshold 0, WICBitmapPaletteTypeCustom
|
||||
%hr7 = call i32 %ini(ptr %c, ptr %fr, ptr @.wic_bgra, i32 0, ptr null, double 0.000000e+00, i32 0)
|
||||
%b7 = icmp slt i32 %hr7, 0
|
||||
br i1 %b7, label %fail, label %size
|
||||
size:
|
||||
%gs = call ptr @w_vt(ptr %c, i64 3)
|
||||
%hr8 = call i32 %gs(ptr %c, ptr %wp, ptr %hp)
|
||||
%w = load i32, ptr %wp
|
||||
%h = load i32, ptr %hp
|
||||
%zw = icmp sle i32 %w, 0
|
||||
%zh = icmp sle i32 %h, 0
|
||||
%zero = or i1 %zw, %zh
|
||||
br i1 %zero, label %fail, label %pixels
|
||||
pixels:
|
||||
%w64 = sext i32 %w to i64
|
||||
%h64 = sext i32 %h to i64
|
||||
%px = mul i64 %w64, %h64
|
||||
%bytesz = mul i64 %px, 4
|
||||
%buf = call ptr @malloc(i64 %bytesz)
|
||||
%stride = mul i32 %w, 4
|
||||
%bsz32 = trunc i64 %bytesz to i32
|
||||
%cpx = call ptr @w_vt(ptr %c, i64 7)
|
||||
%hr9 = call i32 %cpx(ptr %c, ptr null, i32 %stride, i32 %bsz32, ptr %buf)
|
||||
%b9 = icmp slt i32 %hr9, 0
|
||||
br i1 %b9, label %freebuf, label %ok
|
||||
freebuf:
|
||||
call void @free(ptr %buf)
|
||||
br label %fail
|
||||
ok:
|
||||
store i32 %w, ptr %wout
|
||||
store i32 %h, ptr %hout
|
||||
%dpd = load double, ptr %dx
|
||||
%dpi0 = fptosi double %dpd to i32
|
||||
%nodpi = icmp slt i32 %dpi0, 1
|
||||
%dpi = select i1 %nodpi, i32 72, i32 %dpi0
|
||||
store i32 %dpi, ptr %dpiout
|
||||
call void @w_release(ptr %cp)
|
||||
call void @w_release(ptr %frp)
|
||||
call void @w_release(ptr %dp)
|
||||
call void @w_release(ptr %sp)
|
||||
call void @w_release(ptr %fp)
|
||||
ret ptr %buf
|
||||
fail:
|
||||
call void @w_release(ptr %cp)
|
||||
call void @w_release(ptr %frp)
|
||||
call void @w_release(ptr %dp)
|
||||
call void @w_release(ptr %sp)
|
||||
call void @w_release(ptr %fp)
|
||||
ret ptr null
|
||||
}
|
||||
|
||||
; ---- the boot splash ---------------------------------------------------------------
|
||||
;
|
||||
; cocoa.ll's splash, on Windows: a borderless, topmost window with the game's artwork,
|
||||
; shown from lp_pak_boot before main and taken down by App.splash_hide(). It takes the
|
||||
; artwork's own size in points (pixels x 72 / its dpi, as NSImage measures it) at the
|
||||
; display's whole-number scale, so a 1440 x 810 image saved at 144 dpi is 720 x 405
|
||||
; points on either platform. The image is composited over the background colour once,
|
||||
; and painted from that on every WM_PAINT.
|
||||
|
||||
@W_splash = internal global ptr null
|
||||
@W_splash_px = internal global ptr null
|
||||
@W_splash_w = internal global i32 0
|
||||
@W_splash_h = internal global i32 0
|
||||
@W_splash_dw = internal global i32 0
|
||||
@W_splash_dh = internal global i32 0
|
||||
|
||||
define internal i64 @w_splash_proc(ptr %hwnd, i32 %msg, i64 %wp, i64 %lp) {
|
||||
entry:
|
||||
%ps = alloca [72 x i8], align 8 ; PAINTSTRUCT
|
||||
%bmi = alloca [44 x i8], align 4
|
||||
switch i32 %msg, label %def [
|
||||
i32 15, label %paint ; WM_PAINT
|
||||
i32 20, label %erase ; WM_ERASEBKGND
|
||||
]
|
||||
erase:
|
||||
ret i64 1
|
||||
paint:
|
||||
%hdc = call ptr @BeginPaint(ptr %hwnd, ptr %ps)
|
||||
%px = load ptr, ptr @W_splash_px
|
||||
%nopx = icmp eq ptr %px, null
|
||||
br i1 %nopx, label %endp, label %blit
|
||||
blit:
|
||||
%w = load i32, ptr @W_splash_w
|
||||
%h = load i32, ptr @W_splash_h
|
||||
%dw = load i32, ptr @W_splash_dw
|
||||
%dh = load i32, ptr @W_splash_dh
|
||||
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
|
||||
%r = call i32 @StretchDIBits(ptr %hdc, i32 0, i32 0, i32 %dw, i32 %dh, i32 0, i32 0, i32 %w, i32 %h, ptr %px, ptr %bmi, i32 0, i32 13369376)
|
||||
br label %endp
|
||||
endp:
|
||||
%e = call i32 @EndPaint(ptr %hwnd, ptr %ps)
|
||||
ret i64 0
|
||||
def:
|
||||
%d = call i64 @DefWindowProcA(ptr %hwnd, i32 %msg, i64 %wp, i64 %lp)
|
||||
ret i64 %d
|
||||
}
|
||||
|
||||
define void @splash_show(ptr %bytes, i32 %len, i32 %bg) {
|
||||
entry:
|
||||
%wp = alloca i32, align 4
|
||||
%hp = alloca i32, align 4
|
||||
%dpip = alloca i32, align 4
|
||||
%wc = alloca [80 x i8], align 8
|
||||
%had = load ptr, ptr @W_splash
|
||||
%again = icmp ne ptr %had, null
|
||||
br i1 %again, label %out, label %decode
|
||||
decode:
|
||||
%px = call ptr @w_decode(ptr %bytes, i32 %len, ptr %wp, ptr %hp, ptr %dpip)
|
||||
%nopx = icmp eq ptr %px, null
|
||||
br i1 %nopx, label %out, label %over
|
||||
over:
|
||||
%w = load i32, ptr %wp
|
||||
%h = load i32, ptr %hp
|
||||
; composite over the background colour, so transparent edges sit on the game's backdrop
|
||||
%bgb = and i32 %bg, 255
|
||||
%bgg0 = lshr i32 %bg, 8
|
||||
%bgg = and i32 %bgg0, 255
|
||||
%bgr0 = lshr i32 %bg, 16
|
||||
%bgr = and i32 %bgr0, 255
|
||||
%w64 = sext i32 %w to i64
|
||||
%h64 = sext i32 %h to i64
|
||||
%n = mul i64 %w64, %h64
|
||||
br label %mix
|
||||
mix:
|
||||
%i = phi i64 [ 0, %over ], [ %i1, %mixb ]
|
||||
%more = icmp slt i64 %i, %n
|
||||
br i1 %more, label %mixb, label %placed
|
||||
mixb:
|
||||
%o = mul i64 %i, 4
|
||||
%pb = getelementptr i8, ptr %px, i64 %o
|
||||
%o1 = add i64 %o, 1
|
||||
%pg = getelementptr i8, ptr %px, i64 %o1
|
||||
%o2 = add i64 %o, 2
|
||||
%pr = getelementptr i8, ptr %px, i64 %o2
|
||||
%o3 = add i64 %o, 3
|
||||
%pa = getelementptr i8, ptr %px, i64 %o3
|
||||
%a8 = load i8, ptr %pa
|
||||
%a = zext i8 %a8 to i32
|
||||
%ia = sub i32 255, %a
|
||||
%sb8 = load i8, ptr %pb
|
||||
%sb = zext i8 %sb8 to i32
|
||||
%mb0 = mul i32 %sb, %a
|
||||
%mb1 = mul i32 %bgb, %ia
|
||||
%mb2 = add i32 %mb0, %mb1
|
||||
%mb = udiv i32 %mb2, 255
|
||||
%tb = trunc i32 %mb to i8
|
||||
store i8 %tb, ptr %pb
|
||||
%sg8 = load i8, ptr %pg
|
||||
%sg = zext i8 %sg8 to i32
|
||||
%mg0 = mul i32 %sg, %a
|
||||
%mg1 = mul i32 %bgg, %ia
|
||||
%mg2 = add i32 %mg0, %mg1
|
||||
%mg = udiv i32 %mg2, 255
|
||||
%tg = trunc i32 %mg to i8
|
||||
store i8 %tg, ptr %pg
|
||||
%sr8 = load i8, ptr %pr
|
||||
%sr = zext i8 %sr8 to i32
|
||||
%mr0 = mul i32 %sr, %a
|
||||
%mr1 = mul i32 %bgr, %ia
|
||||
%mr2 = add i32 %mr0, %mr1
|
||||
%mr = udiv i32 %mr2, 255
|
||||
%tr = trunc i32 %mr to i8
|
||||
store i8 %tr, ptr %pr
|
||||
store i8 -1, ptr %pa
|
||||
%i1 = add i64 %i, 1
|
||||
br label %mix
|
||||
placed:
|
||||
store ptr %px, ptr @W_splash_px
|
||||
store i32 %w, ptr @W_splash_w
|
||||
store i32 %h, ptr @W_splash_h
|
||||
; points = pixels x 72 / dpi; pixels on screen = points x the display's scale
|
||||
%dpi = load i32, ptr %dpip
|
||||
%pw0 = mul i32 %w, 72
|
||||
%ptw = sdiv i32 %pw0, %dpi
|
||||
%ph0 = mul i32 %h, 72
|
||||
%pth = sdiv i32 %ph0, %dpi
|
||||
%d0 = call i32 @SetProcessDpiAwarenessContext(ptr inttoptr (i64 -4 to ptr))
|
||||
%sysdpi = call i32 @GetDpiForSystem()
|
||||
%ds = call i32 @w_fit_scale(i32 %sysdpi, i32 %ptw, i32 %pth)
|
||||
%dw = mul i32 %ptw, %ds
|
||||
%dh = mul i32 %pth, %ds
|
||||
store i32 %dw, ptr @W_splash_dw
|
||||
store i32 %dh, ptr @W_splash_dh
|
||||
%inst = call ptr @GetModuleHandleA(ptr null)
|
||||
call void @llvm.memset.p0.i64(ptr %wc, i8 0, i64 80, i1 false)
|
||||
store i32 80, ptr %wc
|
||||
%wwp = getelementptr i8, ptr %wc, i64 8
|
||||
store ptr @w_splash_proc, 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))
|
||||
%whc = getelementptr i8, ptr %wc, i64 40
|
||||
store ptr %arrow, ptr %whc
|
||||
%wcn = getelementptr i8, ptr %wc, i64 64
|
||||
store ptr @.w_splash_class, ptr %wcn
|
||||
%atom = call i16 @RegisterClassExA(ptr %wc)
|
||||
%sw = call i32 @GetSystemMetrics(i32 0)
|
||||
%sh = call i32 @GetSystemMetrics(i32 1)
|
||||
%xw = sub i32 %sw, %dw
|
||||
%x = sdiv i32 %xw, 2
|
||||
%yh = sub i32 %sh, %dh
|
||||
%y = sdiv i32 %yh, 2
|
||||
; WS_EX_TOPMOST | WS_EX_TOOLWINDOW (no taskbar button), WS_POPUP | WS_VISIBLE
|
||||
%win = call ptr @CreateWindowExA(i32 136, ptr @.w_splash_class, ptr @.w_empty, i32 -1879048192, i32 %x, i32 %y, i32 %dw, i32 %dh, ptr null, ptr null, ptr %inst, ptr null)
|
||||
%nowin = icmp eq ptr %win, null
|
||||
br i1 %nowin, label %out, label %show
|
||||
show:
|
||||
store ptr %win, ptr @W_splash
|
||||
; paint it now: the game is about to spend a while loading and pumps nothing meanwhile
|
||||
%u = call i32 @UpdateWindow(ptr %win)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @splash_hide() {
|
||||
entry:
|
||||
%win = load ptr, ptr @W_splash
|
||||
%none = icmp eq ptr %win, null
|
||||
br i1 %none, label %out, label %go
|
||||
go:
|
||||
%d = call i32 @DestroyWindow(ptr %win)
|
||||
store ptr null, ptr @W_splash
|
||||
%px = load ptr, ptr @W_splash_px
|
||||
call void @free(ptr %px)
|
||||
store ptr null, ptr @W_splash_px
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; ---- the window icon ---------------------------------------------------------------
|
||||
;
|
||||
; App.set_icon(path): the title bar and the taskbar button of a build with no icon
|
||||
; resource - which is every `ludic build`; a bundled .exe carries its own. The image
|
||||
; becomes an HICON from a 32-bit DIB with its alpha, set on the window now, or as soon
|
||||
; as win_open makes one.
|
||||
|
||||
@W_icon = internal global ptr null
|
||||
|
||||
declare void @free(ptr)
|
||||
declare ptr @malloc(i64)
|
||||
|
||||
define internal void @w_apply_icon() {
|
||||
entry:
|
||||
%hwnd = load ptr, ptr @W_hwnd
|
||||
%icon = load ptr, ptr @W_icon
|
||||
%nw = icmp eq ptr %hwnd, null
|
||||
%ni = icmp eq ptr %icon, null
|
||||
%skip = or i1 %nw, %ni
|
||||
br i1 %skip, label %out, label %set
|
||||
set:
|
||||
%iv = ptrtoint ptr %icon to i64
|
||||
%r1 = call i64 @SendMessageA(ptr %hwnd, i32 128, i64 1, i64 %iv) ; WM_SETICON, ICON_BIG
|
||||
%r0 = call i64 @SendMessageA(ptr %hwnd, i32 128, i64 0, i64 %iv) ; ICON_SMALL
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @app_set_icon(ptr %bytes, i32 %len) {
|
||||
entry:
|
||||
%wp = alloca i32, align 4
|
||||
%hp = alloca i32, align 4
|
||||
%dpip = alloca i32, align 4
|
||||
%bmi = alloca [44 x i8], align 4
|
||||
%bitsp = alloca ptr, align 8
|
||||
%ii = alloca [32 x i8], align 8 ; ICONINFO: fIcon 0, hbmMask 16, hbmColor 24
|
||||
%px = call ptr @w_decode(ptr %bytes, i32 %len, ptr %wp, ptr %hp, ptr %dpip)
|
||||
%nopx = icmp eq ptr %px, null
|
||||
br i1 %nopx, label %out, label %dib
|
||||
dib:
|
||||
%w = load i32, ptr %wp
|
||||
%h = load i32, ptr %hp
|
||||
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
|
||||
%bpl = getelementptr i8, ptr %bmi, i64 12
|
||||
store i16 1, ptr %bpl
|
||||
%bbc = getelementptr i8, ptr %bmi, i64 14
|
||||
store i16 32, ptr %bbc
|
||||
store ptr null, ptr %bitsp
|
||||
%color = call ptr @CreateDIBSection(ptr null, ptr %bmi, i32 0, ptr %bitsp, ptr null, i32 0)
|
||||
%nocolor = icmp eq ptr %color, null
|
||||
br i1 %nocolor, label %freepx, label %fill
|
||||
fill:
|
||||
%bits = load ptr, ptr %bitsp
|
||||
%w64 = sext i32 %w to i64
|
||||
%h64 = sext i32 %h to i64
|
||||
%n = mul i64 %w64, %h64
|
||||
%nb = mul i64 %n, 4
|
||||
call void @llvm.memcpy.p0.p0.i64(ptr %bits, ptr %px, i64 %nb, i1 false)
|
||||
; with an alpha channel in the colour bitmap, an all-zero mask leaves every pixel to it
|
||||
%mask = call ptr @CreateBitmap(i32 %w, i32 %h, i32 1, i32 1, ptr null)
|
||||
call void @llvm.memset.p0.i64(ptr %ii, i8 0, i64 32, i1 false)
|
||||
store i32 1, ptr %ii
|
||||
%mp = getelementptr i8, ptr %ii, i64 16
|
||||
store ptr %mask, ptr %mp
|
||||
%cp = getelementptr i8, ptr %ii, i64 24
|
||||
store ptr %color, ptr %cp
|
||||
%icon = call ptr @CreateIconIndirect(ptr %ii)
|
||||
%d1 = call i32 @DeleteObject(ptr %mask)
|
||||
%d2 = call i32 @DeleteObject(ptr %color)
|
||||
%noicon = icmp eq ptr %icon, null
|
||||
br i1 %noicon, label %freepx, label %keep
|
||||
keep:
|
||||
store ptr %icon, ptr @W_icon
|
||||
call void @w_apply_icon()
|
||||
br label %freepx
|
||||
freepx:
|
||||
call void @free(ptr %px)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,9 +203,6 @@ entry {
|
|||
if emit_ir { ir_flush(out); return }
|
||||
if not has_target { ir_flush(null); return }
|
||||
|
||||
# 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) {
|
||||
|
|
@ -243,7 +240,8 @@ entry {
|
|||
let win32 = join_path(home, "runtime/native/win32.ll")
|
||||
# xinput.lib carries an import named DllMain, which lld skips with a warning on every
|
||||
# link; it is the SDK's, not a mistake in the program, so it is silenced
|
||||
cmd = `{cmd} {win32} -luser32 -lgdi32 -lxinput -Wl,/ignore:importeddllmain`
|
||||
# ole32 for WIC, which decodes the splash and the window icon
|
||||
cmd = `{cmd} {win32} -luser32 -lgdi32 -lxinput -lole32 -Wl,/ignore:importeddllmain`
|
||||
if g_uses_audio {
|
||||
let audiow = join_path(home, "runtime/native/audio_win.ll")
|
||||
cmd = `{cmd} {audiow} -lxaudio2 -lole32`
|
||||
|
|
@ -268,9 +266,15 @@ entry {
|
|||
# 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.
|
||||
# On Windows the transport is http_win.ll, over WinHTTP.
|
||||
if g_uses_http {
|
||||
let http = join_path(home, "runtime/native/http.ll")
|
||||
cmd = `{cmd} {http} -Wl,-needed_framework,Foundation`
|
||||
if g_target_win {
|
||||
let httpw = join_path(home, "runtime/native/http_win.ll")
|
||||
cmd = `{cmd} {httpw} -lwinhttp`
|
||||
} else {
|
||||
let http = join_path(home, "runtime/native/http.ll")
|
||||
cmd = `{cmd} {http} -Wl,-needed_framework,Foundation`
|
||||
}
|
||||
}
|
||||
# Gl.* links the OpenGL backend: gl.ll (offscreen contexts, float helpers), the
|
||||
# generated per-entry-point ABI thunks, and OpenGL.framework. Windowed or not.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue