fix(window): the title a program passes to gl_open / gvk_open reaches the window
The runtime opens a game's window before main, titled with the program's name, and gl_open attached to it without passing its title on (render3d's gvk_open called win_open, which returns early on Windows and opened a second window on macOS). win_set_title in cocoa.ll / win32.ll (setTitle: / SetWindowTextW, UTF-8 -> UTF-16; a no-op without a window, stubbed headless) retitles it from gl_open, and win_open on an open window retitles it on both platforms. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
bb267ff3d5
commit
1256bbfec7
7 changed files with 93 additions and 3 deletions
8
changes/window-title.md
Normal file
8
changes/window-title.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
bump: patch
|
||||||
|
type: fix
|
||||||
|
A game's window takes the title it asks for. The runtime opens a game's window before `main`,
|
||||||
|
titled with the `program` name, and `Gl.open` / render3d's `gl_open` and `gvk_open` attached
|
||||||
|
to it without passing their title on, so a `program Valley` that opened "Maroon Lake" showed
|
||||||
|
"Valley". `win_set_title` (cocoa.ll `setTitle:`, win32.ll `SetWindowTextW`, UTF-8 to UTF-16)
|
||||||
|
now retitles it, and a second `win_open` on macOS retitles the open window instead of making
|
||||||
|
another, as it already did on Windows.
|
||||||
|
|
@ -1411,7 +1411,7 @@ function gvk_open(w: int, h: int, title: string) -> bool {
|
||||||
gl_w = w
|
gl_w = w
|
||||||
gl_h = h
|
gl_h = h
|
||||||
if is_windowed() {
|
if is_windowed() {
|
||||||
win_open(w, h, 1, title)
|
win_open(w, h, 1, title) # the window exists before main: this retitles it
|
||||||
win_gl_resize(w, h)
|
win_gl_resize(w, h)
|
||||||
if gvk_size_buf == null { gvk_size_buf = words(4) }
|
if gvk_size_buf == null { gvk_size_buf = words(4) }
|
||||||
win_gl_drawable(gvk_size_buf)
|
win_gl_drawable(gvk_size_buf)
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,15 @@ entry:
|
||||||
store i32 %w, ptr @W_fbw
|
store i32 %w, ptr @W_fbw
|
||||||
store i32 %h, ptr @W_fbh
|
store i32 %h, ptr @W_fbh
|
||||||
store i32 %scale, ptr @W_scale
|
store i32 %scale, ptr @W_scale
|
||||||
|
; a window already open (the runtime opens a game's before main) is retitled, not
|
||||||
|
; opened again - as win32.ll's win_open does
|
||||||
|
%have = load ptr, ptr @W_win
|
||||||
|
%again = icmp ne ptr %have, null
|
||||||
|
br i1 %again, label %retitle, label %make
|
||||||
|
retitle:
|
||||||
|
call void @win_set_title(ptr %title)
|
||||||
|
ret void
|
||||||
|
make:
|
||||||
%appcls = call ptr @objc_getClass(ptr @.c_app)
|
%appcls = call ptr @objc_getClass(ptr @.c_app)
|
||||||
%sel_sh = call ptr @sel_registerName(ptr @.s_shared)
|
%sel_sh = call ptr @sel_registerName(ptr @.s_shared)
|
||||||
%app = call ptr @objc_msgSend(ptr %appcls, ptr %sel_sh)
|
%app = call ptr @objc_msgSend(ptr %appcls, ptr %sel_sh)
|
||||||
|
|
@ -1700,6 +1708,31 @@ out:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; ---- win_set_title --------------------------------------------------------------------
|
||||||
|
; Retitle the window the runtime opened before main: gl_open / gvk_open attach to that window
|
||||||
|
; rather than opening one, and the title the program asked for would otherwise be dropped in
|
||||||
|
; favour of the program's name. A no-op without a window.
|
||||||
|
define void @win_set_title(ptr %title) {
|
||||||
|
entry:
|
||||||
|
%win = load ptr, ptr @W_win
|
||||||
|
%nowin = icmp eq ptr %win, null
|
||||||
|
%notitle = icmp eq ptr %title, null
|
||||||
|
%skip = or i1 %nowin, %notitle
|
||||||
|
br i1 %skip, label %out, label %go
|
||||||
|
go:
|
||||||
|
%strcls = call ptr @objc_getClass(ptr @.c_str)
|
||||||
|
%sel_utf8 = call ptr @sel_registerName(ptr @.s_utf8)
|
||||||
|
%ns = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %strcls, ptr %sel_utf8, ptr %title)
|
||||||
|
%none = icmp eq ptr %ns, null
|
||||||
|
br i1 %none, label %out, label %set
|
||||||
|
set:
|
||||||
|
%sel_title = call ptr @sel_registerName(ptr @.s_title)
|
||||||
|
%r = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %win, ptr %sel_title, ptr %ns)
|
||||||
|
br label %out
|
||||||
|
out:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
; ---- App.window_hide / App.window_show ------------------------------------------------
|
; ---- App.window_hide / App.window_show ------------------------------------------------
|
||||||
; The game's window leaves the screen and comes back with its view, layer and GL context
|
; The game's window leaves the screen and comes back with its view, layer and GL context
|
||||||
; untouched: a launcher steps aside while the game it started runs. The run does not end
|
; untouched: a launcher steps aside while the game it started runs. The run does not end
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,12 @@ entry:
|
||||||
}
|
}
|
||||||
; the drawable's size, for a graphics API that follows the window without a GL context (render3d's
|
; the drawable's size, for a graphics API that follows the window without a GL context (render3d's
|
||||||
; Vulkan resize check); headless there is no window, so 0 x 0. cocoa.ll's real one wins when linked.
|
; Vulkan resize check); headless there is no window, so 0 x 0. cocoa.ll's real one wins when linked.
|
||||||
|
; win_set_title: cocoa.ll's in a windowed build; nothing to retitle headless
|
||||||
|
define weak void @win_set_title(ptr %title) {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
define weak void @win_gl_drawable(ptr %out) {
|
define weak void @win_gl_drawable(ptr %out) {
|
||||||
entry:
|
entry:
|
||||||
store i32 0, ptr %out
|
store i32 0, ptr %out
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import "gl_api.ludic"
|
||||||
|
|
||||||
# ---- native glue (cocoa.ll / gl.ll) -------------------------------------------
|
# ---- native glue (cocoa.ll / gl.ll) -------------------------------------------
|
||||||
extern function win_gl_attach() -> int = "win_gl_attach"
|
extern function win_gl_attach() -> int = "win_gl_attach"
|
||||||
|
extern function win_set_title(title: pointer) -> void = "win_set_title"
|
||||||
extern function win_gl_resize(w: int, h: int) = "win_gl_resize"
|
extern function win_gl_resize(w: int, h: int) = "win_gl_resize"
|
||||||
extern function win_gl_swap() = "win_gl_swap"
|
extern function win_gl_swap() = "win_gl_swap"
|
||||||
extern function win_gl_scale() -> int = "win_gl_scale"
|
extern function win_gl_scale() -> int = "win_gl_scale"
|
||||||
|
|
@ -91,6 +92,10 @@ function gl_open(width: int, height: int, title: pointer) -> bool {
|
||||||
if win_gl_attach() == 0 {
|
if win_gl_attach() == 0 {
|
||||||
win_open(width, height, 1, title) # a plain program: no window yet
|
win_open(width, height, 1, title) # a plain program: no window yet
|
||||||
if win_gl_attach() == 0 { return false }
|
if win_gl_attach() == 0 { return false }
|
||||||
|
} else {
|
||||||
|
# a game's window was opened before main, titled with the program's name: give it
|
||||||
|
# the title this call asks for
|
||||||
|
win_set_title(title)
|
||||||
}
|
}
|
||||||
win_gl_resize(width, height)
|
win_gl_resize(width, height)
|
||||||
gl_scale = win_gl_scale()
|
gl_scale = win_gl_scale()
|
||||||
|
|
|
||||||
|
|
@ -52,3 +52,8 @@ define ptr @win_native_instance() {
|
||||||
entry:
|
entry:
|
||||||
ret ptr null
|
ret ptr null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define void @win_set_title(ptr %title) {
|
||||||
|
entry:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ declare i64 @DefWindowProcA(ptr, i32, i64, i64)
|
||||||
declare i32 @ShowWindow(ptr, i32)
|
declare i32 @ShowWindow(ptr, i32)
|
||||||
declare i32 @SetForegroundWindow(ptr)
|
declare i32 @SetForegroundWindow(ptr)
|
||||||
declare i32 @BringWindowToTop(ptr)
|
declare i32 @BringWindowToTop(ptr)
|
||||||
|
declare i32 @SetWindowTextW(ptr, ptr)
|
||||||
|
declare i32 @MultiByteToWideChar(i32, i32, ptr, i32, ptr, i32)
|
||||||
declare i32 @PeekMessageA(ptr, ptr, i32, i32, i32)
|
declare i32 @PeekMessageA(ptr, ptr, i32, i32, i32)
|
||||||
declare i64 @DispatchMessageA(ptr)
|
declare i64 @DispatchMessageA(ptr)
|
||||||
declare ptr @GetDC(ptr)
|
declare ptr @GetDC(ptr)
|
||||||
|
|
@ -188,7 +190,11 @@ entry:
|
||||||
store i32 %scale, ptr @W_scale
|
store i32 %scale, ptr @W_scale
|
||||||
%have = load ptr, ptr @W_hwnd
|
%have = load ptr, ptr @W_hwnd
|
||||||
%again = icmp ne ptr %have, null
|
%again = icmp ne ptr %have, null
|
||||||
br i1 %again, label %out, label %make
|
br i1 %again, label %retitle, label %make
|
||||||
|
retitle:
|
||||||
|
; already open (before main, for a game): take the title this call asks for
|
||||||
|
call void @win_set_title(ptr %title)
|
||||||
|
br label %out
|
||||||
make:
|
make:
|
||||||
; DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4
|
; DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4
|
||||||
%dpi = call i32 @SetProcessDpiAwarenessContext(ptr inttoptr (i64 -4 to ptr))
|
%dpi = call i32 @SetProcessDpiAwarenessContext(ptr inttoptr (i64 -4 to ptr))
|
||||||
|
|
@ -268,6 +274,33 @@ out:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; ---- win_set_title --------------------------------------------------------------------
|
||||||
|
; Retitle the window opened before main (gl_open / gvk_open attach to it rather than opening
|
||||||
|
; one, and would otherwise leave the program's name on it). UTF-8 in, UTF-16 to the window.
|
||||||
|
; A no-op without a window.
|
||||||
|
define void @win_set_title(ptr %title) {
|
||||||
|
entry:
|
||||||
|
%h = load ptr, ptr @W_hwnd
|
||||||
|
%nowin = icmp eq ptr %h, null
|
||||||
|
%notitle = icmp eq ptr %title, null
|
||||||
|
%skip = or i1 %nowin, %notitle
|
||||||
|
br i1 %skip, label %out, label %size
|
||||||
|
size:
|
||||||
|
%n = call i32 @MultiByteToWideChar(i32 65001, i32 0, ptr %title, i32 -1, ptr null, i32 0)
|
||||||
|
%bad = icmp sle i32 %n, 0
|
||||||
|
br i1 %bad, label %out, label %conv
|
||||||
|
conv:
|
||||||
|
%n64 = sext i32 %n to i64
|
||||||
|
%bytes = mul i64 %n64, 2
|
||||||
|
%w = call ptr @malloc(i64 %bytes)
|
||||||
|
%c = call i32 @MultiByteToWideChar(i32 65001, i32 0, ptr %title, i32 -1, ptr %w, i32 %n)
|
||||||
|
%r = call i32 @SetWindowTextW(ptr %h, ptr %w)
|
||||||
|
call void @free(ptr %w)
|
||||||
|
br label %out
|
||||||
|
out:
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
; ---- App.window_hide / App.window_show ------------------------------------------------
|
; ---- App.window_hide / App.window_show ------------------------------------------------
|
||||||
; The game's window leaves the screen and comes back with its GL context or Vulkan surface
|
; The game's window leaves the screen and comes back with its GL context or Vulkan surface
|
||||||
; untouched: a launcher steps aside while the game it started runs. A hidden window is
|
; untouched: a launcher steps aside while the game it started runs. A hidden window is
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue