fix(win32 gui): set window title after gui window is created

This commit is contained in:
CJ van den Berg 2025-01-06 16:49:57 +01:00
parent 206e23a603
commit 93d6ee2626
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 23 additions and 68 deletions

View file

@ -30,6 +30,8 @@ dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
thread: ?std.Thread = null,
title_buf: std.ArrayList(u16),
const global = struct {
var init_called: bool = false;
};
@ -59,10 +61,11 @@ pub fn init(
},
.system_clipboard_allocator = allocator,
};
var result = .{
var result: Self = .{
.allocator = allocator,
.vx = try vaxis.init(allocator, opts),
.handler_ctx = handler_ctx,
.title_buf = std.ArrayList(u16).init(allocator),
};
result.vx.caps.unicode = .unicode;
result.vx.screen.width_method = .unicode;
@ -73,6 +76,7 @@ pub fn deinit(self: *Self) void {
std.log.warn("TODO: implement win32 renderer deinit", .{});
var drop_writer = DropWriter{};
self.vx.deinit(self.allocator, drop_writer.writer().any());
self.title_buf.deinit();
}
threadlocal var thread_is_panicing = false;
@ -203,6 +207,8 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) !void {
var buf: [200]u8 = undefined;
if (self.dispatch_event) |f| f(self.handler_ctx, fmtmsg(&buf, .{"resize"}));
}
if (self.title_buf.items.len > 0)
self.set_terminal_title_internal();
return;
}
}
@ -305,65 +311,22 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) !void {
return error.UnexpectedRendererEvent;
}
fn setEllipsis(str: []u16) void {
std.debug.assert(str.len >= 3);
str[str.len - 1] = '.';
str[str.len - 2] = '.';
str[str.len - 3] = '.';
}
const ConversionSizes = struct {
src_len: usize,
dst_len: usize,
};
fn calcUtf8ToUtf16LeWithMax(utf8: []const u8, max_dst_len: usize) !ConversionSizes {
var src_len: usize = 0;
var dst_len: usize = 0;
while (src_len < utf8.len) {
if (dst_len >= max_dst_len) break;
const n = try std.unicode.utf8ByteSequenceLength(utf8[src_len]);
const next_src_len = src_len + n;
const codepoint = try std.unicode.utf8Decode(utf8[src_len..next_src_len]);
if (codepoint < 0x10000) {
dst_len += 1;
} else {
if (dst_len + 2 > max_dst_len) break;
dst_len += 2;
}
src_len = next_src_len;
}
return .{ .src_len = src_len, .dst_len = dst_len };
}
pub fn set_terminal_title(self: *Self, title_utf8: []const u8) void {
_ = self;
const max_title_wide = 500;
const conversion_sizes = calcUtf8ToUtf16LeWithMax(title_utf8, max_title_wide) catch {
pub fn set_terminal_title(self: *Self, text: []const u8) void {
std.unicode.utf8ToUtf16LeArrayList(&self.title_buf, text) catch {
std.log.err("title is invalid UTF-8", .{});
return;
};
self.set_terminal_title_internal();
}
var title_wide_buf: [max_title_wide + 1]u16 = undefined;
const len = @min(max_title_wide, conversion_sizes.dst_len);
title_wide_buf[len] = 0;
const title_wide = title_wide_buf[0..len :0];
const size = std.unicode.utf8ToUtf16Le(title_wide, title_utf8[0..conversion_sizes.src_len]) catch |err| switch (err) {
error.InvalidUtf8 => {
std.log.err("title is invalid UTF-8", .{});
return;
},
};
std.debug.assert(size == conversion_sizes.dst_len);
if (conversion_sizes.src_len != title_utf8.len) {
setEllipsis(title_wide);
}
var win32_err: gui.Win32Error = undefined;
gui.setWindowTitle(title_wide, &win32_err) catch |err| switch (err) {
error.NoWindow => std.log.warn("no window to set the title for", .{}),
error.Win32 => std.log.err("{s} failed with {}", .{ win32_err.what, win32_err.code.fmt() }),
fn set_terminal_title_internal(self: *Self) void {
const title = self.title_buf.toOwnedSliceSentinel(0) catch @panic("OOM:set_terminal_title");
gui.set_window_title(title) catch {
// leave self.title_buf to try again later
self.title_buf = std.ArrayList(u16).fromOwnedSlice(self.allocator, title);
return;
};
self.allocator.free(title);
}
pub fn set_terminal_style(self: *Self, style_: Style) void {

View file

@ -485,27 +485,19 @@ fn entry(pid: thespian.pid) !void {
pid.send(.{"quit"}) catch |e| onexit(e);
}
pub const Win32Error = struct {
what: [:0]const u8,
code: win32.WIN32_ERROR,
pub fn set(self: *Win32Error, what: [:0]const u8, code: win32.WIN32_ERROR) error{Win32} {
self.* = .{ .what = what, .code = code };
return error.Win32;
}
};
pub fn stop() void {
const hwnd = global.hwnd orelse return;
_ = win32.SendMessageW(hwnd, WM_APP_EXIT, 0, 0);
}
pub fn setWindowTitle(title: [*:0]const u16, err: *Win32Error) error{ NoWindow, Win32 }!void {
pub fn set_window_title(title: [*:0]const u16) error{ NoWindow, Win32 }!void {
global.mutex.lock();
defer global.mutex.unlock();
const hwnd = global.hwnd orelse return error.NoWindow;
if (0 == win32.SetWindowTextW(hwnd, title))
return err.set("SetWindowText", win32.GetLastError());
if (win32.SetWindowTextW(hwnd, title) == 0) {
std.log.warn("error in SetWindowText: {}", .{win32.GetLastError()});
return error.Win32;
}
}
// returns false if there is no hwnd