win32 gui: rework startup/hwnd sync
This commit is contained in:
parent
ff7bdeef6b
commit
337b6ce626
5 changed files with 137 additions and 115 deletions
|
@ -71,7 +71,8 @@
|
||||||
"on_match_failure": "ignore",
|
"on_match_failure": "ignore",
|
||||||
"press": [
|
"press": [
|
||||||
["ctrl+h ctrl+a", "open_help"],
|
["ctrl+h ctrl+a", "open_help"],
|
||||||
["ctrl+x ctrl+f", "open_recent"],
|
["ctrl+x ctrl+f", "open_file"],
|
||||||
|
["ctrl+x b", "open_recent"],
|
||||||
["alt+x", "open_command_palette"],
|
["alt+x", "open_command_palette"],
|
||||||
["ctrl+x ctrl+c", "quit"]
|
["ctrl+x ctrl+c", "quit"]
|
||||||
]
|
]
|
||||||
|
|
|
@ -39,7 +39,7 @@ logger: log.Logger,
|
||||||
|
|
||||||
loop: Loop,
|
loop: Loop,
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool) !Self {
|
pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) !Self {
|
||||||
const opts: vaxis.Vaxis.Options = .{
|
const opts: vaxis.Vaxis.Options = .{
|
||||||
.kitty_keyboard_flags = .{
|
.kitty_keyboard_flags = .{
|
||||||
.disambiguate = true,
|
.disambiguate = true,
|
||||||
|
|
|
@ -23,6 +23,7 @@ allocator: std.mem.Allocator,
|
||||||
vx: vaxis.Vaxis,
|
vx: vaxis.Vaxis,
|
||||||
|
|
||||||
handler_ctx: *anyopaque,
|
handler_ctx: *anyopaque,
|
||||||
|
dispatch_initialized: *const fn (ctx: *anyopaque) void,
|
||||||
dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||||
dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null,
|
dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null,
|
||||||
dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null,
|
dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null,
|
||||||
|
@ -30,7 +31,9 @@ dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null,
|
||||||
|
|
||||||
thread: ?std.Thread = null,
|
thread: ?std.Thread = null,
|
||||||
|
|
||||||
|
hwnd: ?win32.HWND = null,
|
||||||
title_buf: std.ArrayList(u16),
|
title_buf: std.ArrayList(u16),
|
||||||
|
style: ?Style = null,
|
||||||
|
|
||||||
const global = struct {
|
const global = struct {
|
||||||
var init_called: bool = false;
|
var init_called: bool = false;
|
||||||
|
@ -44,6 +47,7 @@ pub fn init(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
handler_ctx: *anyopaque,
|
handler_ctx: *anyopaque,
|
||||||
no_alternate: bool,
|
no_alternate: bool,
|
||||||
|
dispatch_initialized: *const fn (ctx: *anyopaque) void,
|
||||||
) !Self {
|
) !Self {
|
||||||
std.debug.assert(!global.init_called);
|
std.debug.assert(!global.init_called);
|
||||||
global.init_called = true;
|
global.init_called = true;
|
||||||
|
@ -66,6 +70,7 @@ pub fn init(
|
||||||
.vx = try vaxis.init(allocator, opts),
|
.vx = try vaxis.init(allocator, opts),
|
||||||
.handler_ctx = handler_ctx,
|
.handler_ctx = handler_ctx,
|
||||||
.title_buf = std.ArrayList(u16).init(allocator),
|
.title_buf = std.ArrayList(u16).init(allocator),
|
||||||
|
.dispatch_initialized = dispatch_initialized,
|
||||||
};
|
};
|
||||||
result.vx.caps.unicode = .unicode;
|
result.vx.caps.unicode = .unicode;
|
||||||
result.vx.screen.width_method = .unicode;
|
result.vx.screen.width_method = .unicode;
|
||||||
|
@ -122,9 +127,13 @@ pub fn fmtmsg(buf: []u8, value: anytype) []const u8 {
|
||||||
|
|
||||||
pub fn render(self: *Self) error{}!void {
|
pub fn render(self: *Self) error{}!void {
|
||||||
_ = gui.updateScreen(&self.vx.screen);
|
_ = gui.updateScreen(&self.vx.screen);
|
||||||
|
if (self.hwnd) |hwnd| win32.invalidateHwnd(hwnd);
|
||||||
}
|
}
|
||||||
pub fn stop(self: *Self) void {
|
pub fn stop(self: *Self) void {
|
||||||
gui.stop();
|
// this is guaranteed because stop won't be called until after
|
||||||
|
// the window is created and we call dispatch_initialized
|
||||||
|
const hwnd = self.hwnd orelse unreachable;
|
||||||
|
gui.stop(hwnd);
|
||||||
if (self.thread) |thread| thread.join();
|
if (self.thread) |thread| thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,8 +216,6 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) !void {
|
||||||
var buf: [200]u8 = undefined;
|
var buf: [200]u8 = undefined;
|
||||||
if (self.dispatch_event) |f| f(self.handler_ctx, fmtmsg(&buf, .{"resize"}));
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,30 +315,57 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) !void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
var hwnd: usize = undefined;
|
||||||
|
if (try cbor.match(msg, .{
|
||||||
|
cbor.any,
|
||||||
|
"WindowCreated",
|
||||||
|
cbor.extract(&hwnd),
|
||||||
|
})) {
|
||||||
|
std.debug.assert(self.hwnd == null);
|
||||||
|
self.hwnd = @ptrFromInt(hwnd);
|
||||||
|
self.dispatch_initialized(self.handler_ctx);
|
||||||
|
self.update_window_title();
|
||||||
|
self.update_window_style();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
return error.UnexpectedRendererEvent;
|
return error.UnexpectedRendererEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminal_title(self: *Self, text: []const u8) void {
|
pub fn set_terminal_title(self: *Self, text: []const u8) void {
|
||||||
|
self.title_buf.clearRetainingCapacity();
|
||||||
std.unicode.utf8ToUtf16LeArrayList(&self.title_buf, text) catch {
|
std.unicode.utf8ToUtf16LeArrayList(&self.title_buf, text) catch {
|
||||||
std.log.err("title is invalid UTF-8", .{});
|
std.log.err("title is invalid UTF-8", .{});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
self.set_terminal_title_internal();
|
self.update_window_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_terminal_title_internal(self: *Self) void {
|
fn update_window_title(self: *Self) void {
|
||||||
const title = self.title_buf.toOwnedSliceSentinel(0) catch @panic("OOM:set_terminal_title");
|
if (self.title_buf.items.len == 0) return;
|
||||||
gui.set_window_title(title) catch {
|
|
||||||
// leave self.title_buf to try again later
|
// keep the title buf around if the window isn't created yet
|
||||||
|
const hwnd = self.hwnd orelse return;
|
||||||
|
|
||||||
|
const title = self.title_buf.toOwnedSliceSentinel(0) catch @panic("OOM:update_window_title");
|
||||||
|
if (win32.SetWindowTextW(hwnd, title) == 0) {
|
||||||
|
std.log.warn("SetWindowText failed with {}", .{win32.GetLastError().fmt()});
|
||||||
self.title_buf = std.ArrayList(u16).fromOwnedSlice(self.allocator, title);
|
self.title_buf = std.ArrayList(u16).fromOwnedSlice(self.allocator, title);
|
||||||
return;
|
} else {
|
||||||
};
|
|
||||||
self.allocator.free(title);
|
self.allocator.free(title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminal_style(self: *Self, style_: Style) void {
|
pub fn set_terminal_style(self: *Self, style_: Style) void {
|
||||||
_ = self;
|
self.style = style_;
|
||||||
if (style_.bg) |color| gui.set_window_background(@intCast(color.color));
|
self.update_window_style();
|
||||||
|
}
|
||||||
|
fn update_window_style(self: *Self) void {
|
||||||
|
const hwnd = self.hwnd orelse return;
|
||||||
|
if (self.style) |style_| {
|
||||||
|
if (style_.bg) |color| gui.set_window_background(hwnd, @intCast(color.color));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_terminal_cursor_color(self: *Self, color: Color) void {
|
pub fn set_terminal_cursor_color(self: *Self, color: Color) void {
|
||||||
|
|
|
@ -106,7 +106,7 @@ fn init(allocator: Allocator) !*Self {
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.config = conf,
|
.config = conf,
|
||||||
.rdr = try renderer.init(allocator, self, tp.env.get().is("no-alternate")),
|
.rdr = try renderer.init(allocator, self, tp.env.get().is("no-alternate"), dispatch_initialized),
|
||||||
.frame_time = frame_time,
|
.frame_time = frame_time,
|
||||||
.frame_clock = frame_clock,
|
.frame_clock = frame_clock,
|
||||||
.frame_clock_running = true,
|
.frame_clock_running = true,
|
||||||
|
@ -115,7 +115,9 @@ fn init(allocator: Allocator) !*Self {
|
||||||
.message_filters = MessageFilter.List.init(allocator),
|
.message_filters = MessageFilter.List.init(allocator),
|
||||||
.input_listeners = EventHandler.List.init(allocator),
|
.input_listeners = EventHandler.List.init(allocator),
|
||||||
.logger = log.logger("tui"),
|
.logger = log.logger("tui"),
|
||||||
.init_timer = try tp.timeout.init_ms(init_delay, tp.message.fmt(.{"init"})),
|
.init_timer = if (build_options.gui) null else try tp.timeout.init_ms(init_delay, tp.message.fmt(
|
||||||
|
.{"init"},
|
||||||
|
)),
|
||||||
.theme = theme,
|
.theme = theme,
|
||||||
.no_sleep = tp.env.get().is("no-sleep"),
|
.no_sleep = tp.env.get().is("no-sleep"),
|
||||||
};
|
};
|
||||||
|
@ -339,7 +341,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
|
||||||
if (self.init_timer) |*timer| {
|
if (self.init_timer) |*timer| {
|
||||||
timer.deinit();
|
timer.deinit();
|
||||||
self.init_timer = null;
|
self.init_timer = null;
|
||||||
} else {
|
} else if (!build_options.gui) {
|
||||||
return tp.unexpected(m);
|
return tp.unexpected(m);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -446,6 +448,13 @@ fn dispatch_flush_input_event(self: *Self) !void {
|
||||||
if (mode.event_handler) |eh| try eh.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"}));
|
if (mode.event_handler) |eh| try eh.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dispatch_initialized(ctx: *anyopaque) void {
|
||||||
|
_ = ctx;
|
||||||
|
tp.self_pid().send(.{"init"}) catch |e| switch (e) {
|
||||||
|
error.Exit => {}, // safe to ignore
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn dispatch_input(ctx: *anyopaque, cbor_msg: []const u8) void {
|
fn dispatch_input(ctx: *anyopaque, cbor_msg: []const u8) void {
|
||||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||||
const m: tp.message = .{ .buf = cbor_msg };
|
const m: tp.message = .{ .buf = cbor_msg };
|
||||||
|
@ -1109,7 +1118,7 @@ pub const fallbacks: []const FallBack = &[_]FallBack{
|
||||||
};
|
};
|
||||||
|
|
||||||
fn set_terminal_style(self: *Self) void {
|
fn set_terminal_style(self: *Self) void {
|
||||||
if (self.config.enable_terminal_color_scheme) {
|
if (build_options.gui or self.config.enable_terminal_color_scheme) {
|
||||||
self.rdr.set_terminal_style(self.theme.editor);
|
self.rdr.set_terminal_style(self.theme.editor);
|
||||||
self.rdr.set_terminal_cursor_color(self.theme.editor_cursor.bg.?);
|
self.rdr.set_terminal_cursor_color(self.theme.editor_cursor.bg.?);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ const cbor = @import("cbor");
|
||||||
const thespian = @import("thespian");
|
const thespian = @import("thespian");
|
||||||
const vaxis = @import("vaxis");
|
const vaxis = @import("vaxis");
|
||||||
|
|
||||||
|
const RGB = @import("color").RGB;
|
||||||
const input = @import("input");
|
const input = @import("input");
|
||||||
const windowmsg = @import("windowmsg.zig");
|
const windowmsg = @import("windowmsg.zig");
|
||||||
|
|
||||||
|
@ -19,6 +20,9 @@ const HResultError = ddui.HResultError;
|
||||||
const WM_APP_EXIT = win32.WM_APP + 1;
|
const WM_APP_EXIT = win32.WM_APP + 1;
|
||||||
const WM_APP_SET_BACKGROUND = win32.WM_APP + 2;
|
const WM_APP_SET_BACKGROUND = win32.WM_APP + 2;
|
||||||
|
|
||||||
|
const WM_APP_EXIT_RESULT = 0x45feaa11;
|
||||||
|
const WM_APP_SET_BACKGROUND_RESULT = 0x369a26cd;
|
||||||
|
|
||||||
pub const DropWriter = struct {
|
pub const DropWriter = struct {
|
||||||
pub const WriteError = error{};
|
pub const WriteError = error{};
|
||||||
pub const Writer = std.io.Writer(DropWriter, WriteError, write);
|
pub const Writer = std.io.Writer(DropWriter, WriteError, write);
|
||||||
|
@ -41,15 +45,18 @@ fn onexit(e: error{Exit}) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
const global = struct {
|
const global = struct {
|
||||||
var mutex: std.Thread.Mutex = .{};
|
|
||||||
|
|
||||||
var init_called: bool = false;
|
var init_called: bool = false;
|
||||||
var start_called: bool = false;
|
var start_called: bool = false;
|
||||||
var icons: Icons = undefined;
|
var icons: Icons = undefined;
|
||||||
var dwrite_factory: *win32.IDWriteFactory = undefined;
|
var dwrite_factory: *win32.IDWriteFactory = undefined;
|
||||||
var d2d_factory: *win32.ID2D1Factory = undefined;
|
var d2d_factory: *win32.ID2D1Factory = undefined;
|
||||||
var window_class: u16 = 0;
|
|
||||||
var hwnd: ?win32.HWND = null;
|
const shared_screen = struct {
|
||||||
|
var mutex: std.Thread.Mutex = .{};
|
||||||
|
// only access arena/obj while the mutex is locked
|
||||||
|
var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
var obj: vaxis.Screen = .{};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
const window_style_ex = win32.WINDOW_EX_STYLE{
|
const window_style_ex = win32.WINDOW_EX_STYLE{
|
||||||
//.ACCEPTFILES = 1,
|
//.ACCEPTFILES = 1,
|
||||||
|
@ -115,7 +122,7 @@ fn d2dColorFromVAxis(color: vaxis.Cell.Color) win32.D2D_COLOR_F {
|
||||||
return switch (color) {
|
return switch (color) {
|
||||||
.default => .{ .r = 0, .g = 0, .b = 0, .a = 0 },
|
.default => .{ .r = 0, .g = 0, .b = 0, .a = 0 },
|
||||||
.index => |idx| blk: {
|
.index => |idx| blk: {
|
||||||
const rgb = @import("color").RGB.from_u24(xterm_colors[idx]);
|
const rgb = RGB.from_u24(xterm_colors[idx]);
|
||||||
break :blk .{
|
break :blk .{
|
||||||
.r = @as(f32, @floatFromInt(rgb.r)) / 255.0,
|
.r = @as(f32, @floatFromInt(rgb.r)) / 255.0,
|
||||||
.g = @as(f32, @floatFromInt(rgb.g)) / 255.0,
|
.g = @as(f32, @floatFromInt(rgb.g)) / 255.0,
|
||||||
|
@ -220,22 +227,7 @@ const State = struct {
|
||||||
text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{},
|
text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{},
|
||||||
scroll_delta: isize = 0,
|
scroll_delta: isize = 0,
|
||||||
currently_rendered_cell_size: ?XY(i32) = null,
|
currently_rendered_cell_size: ?XY(i32) = null,
|
||||||
|
background: ?u32 = null,
|
||||||
// these fields should only be accessed inside the global mutex
|
|
||||||
shared_screen_arena: std.heap.ArenaAllocator,
|
|
||||||
shared_screen: vaxis.Screen = .{},
|
|
||||||
pub fn deinit(self: *State) void {
|
|
||||||
{
|
|
||||||
global.mutex.lock();
|
|
||||||
defer global.mutex.unlock();
|
|
||||||
self.shared_screen.deinit(self.shared_screen_arena.allocator());
|
|
||||||
self.shared_screen_arena.deinit();
|
|
||||||
}
|
|
||||||
if (self.maybe_d2d) |*d2d| {
|
|
||||||
d2d.deinit();
|
|
||||||
}
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
fn stateFromHwnd(hwnd: win32.HWND) *State {
|
fn stateFromHwnd(hwnd: win32.HWND) *State {
|
||||||
const addr: usize = @bitCast(win32.GetWindowLongPtrW(hwnd, @enumFromInt(0)));
|
const addr: usize = @bitCast(win32.GetWindowLongPtrW(hwnd, @enumFromInt(0)));
|
||||||
|
@ -245,12 +237,13 @@ fn stateFromHwnd(hwnd: win32.HWND) *State {
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
d2d: *const D2d,
|
d2d: *const D2d,
|
||||||
|
background: RGB,
|
||||||
screen: *const vaxis.Screen,
|
screen: *const vaxis.Screen,
|
||||||
text_format_editor: *win32.IDWriteTextFormat,
|
text_format_editor: *win32.IDWriteTextFormat,
|
||||||
cell_size: XY(i32),
|
cell_size: XY(i32),
|
||||||
) void {
|
) void {
|
||||||
{
|
{
|
||||||
const color = ddui.rgb8(31, 31, 31);
|
const color = ddui.rgb8(background.r, background.g, background.b);
|
||||||
d2d.target.ID2D1RenderTarget.Clear(&color);
|
d2d.target.ID2D1RenderTarget.Clear(&color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +399,6 @@ fn entry(pid: thespian.pid) !void {
|
||||||
global.icons = getIcons(initial_placement.dpi);
|
global.icons = getIcons(initial_placement.dpi);
|
||||||
|
|
||||||
// we only need to register the window class once per process
|
// we only need to register the window class once per process
|
||||||
if (global.window_class == 0) {
|
|
||||||
const wc = win32.WNDCLASSEXW{
|
const wc = win32.WNDCLASSEXW{
|
||||||
.cbSize = @sizeOf(win32.WNDCLASSEXW),
|
.cbSize = @sizeOf(win32.WNDCLASSEXW),
|
||||||
.style = .{},
|
.style = .{},
|
||||||
|
@ -421,12 +413,10 @@ fn entry(pid: thespian.pid) !void {
|
||||||
.lpszClassName = CLASS_NAME,
|
.lpszClassName = CLASS_NAME,
|
||||||
.hIconSm = global.icons.small,
|
.hIconSm = global.icons.small,
|
||||||
};
|
};
|
||||||
global.window_class = win32.RegisterClassExW(&wc);
|
if (0 == win32.RegisterClassExW(&wc)) fatalWin32(
|
||||||
if (global.window_class == 0) fatalWin32(
|
|
||||||
"RegisterClass for main window",
|
"RegisterClass for main window",
|
||||||
win32.GetLastError(),
|
win32.GetLastError(),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
var create_args = CreateWindowArgs{
|
var create_args = CreateWindowArgs{
|
||||||
.allocator = arena_instance.allocator(),
|
.allocator = arena_instance.allocator(),
|
||||||
|
@ -446,20 +436,14 @@ fn entry(pid: thespian.pid) !void {
|
||||||
win32.GetModuleHandleW(null),
|
win32.GetModuleHandleW(null),
|
||||||
@ptrCast(&create_args),
|
@ptrCast(&create_args),
|
||||||
) orelse fatalWin32("CreateWindow", win32.GetLastError());
|
) orelse fatalWin32("CreateWindow", win32.GetLastError());
|
||||||
defer if (0 == win32.DestroyWindow(hwnd)) fatalWin32("DestroyWindow", win32.GetLastError());
|
// NEVER DESTROY THE WINDOW!
|
||||||
|
// This allows us to send the hwnd to other thread/parts
|
||||||
{
|
// of the app and it will always be valid.
|
||||||
global.mutex.lock();
|
pid.send(.{
|
||||||
defer global.mutex.unlock();
|
"RDR",
|
||||||
std.debug.assert(global.hwnd == null);
|
"WindowCreated",
|
||||||
global.hwnd = hwnd;
|
@intFromPtr(hwnd),
|
||||||
}
|
}) catch |e| return onexit(e);
|
||||||
defer {
|
|
||||||
global.mutex.lock();
|
|
||||||
defer global.mutex.unlock();
|
|
||||||
std.debug.assert(global.hwnd == hwnd);
|
|
||||||
global.hwnd = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// TODO: maybe use DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 if applicable
|
// TODO: maybe use DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 if applicable
|
||||||
|
@ -491,42 +475,33 @@ fn entry(pid: thespian.pid) !void {
|
||||||
pid.send(.{"quit"}) catch |e| onexit(e);
|
pid.send(.{"quit"}) catch |e| onexit(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop() void {
|
pub fn stop(hwnd: win32.HWND) void {
|
||||||
const hwnd = global.hwnd orelse return;
|
std.debug.assert(WM_APP_EXIT_RESULT == win32.SendMessageW(hwnd, WM_APP_EXIT, 0, 0));
|
||||||
_ = win32.SendMessageW(hwnd, WM_APP_EXIT, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_title(title: [*:0]const u16) error{ NoWindow, Win32 }!void {
|
pub fn set_window_background(hwnd: win32.HWND, color: u32) void {
|
||||||
global.mutex.lock();
|
std.debug.assert(WM_APP_SET_BACKGROUND_RESULT == win32.SendMessageW(
|
||||||
defer global.mutex.unlock();
|
hwnd,
|
||||||
const hwnd = global.hwnd orelse return error.NoWindow;
|
WM_APP_SET_BACKGROUND,
|
||||||
if (win32.SetWindowTextW(hwnd, title) == 0) {
|
color,
|
||||||
std.log.warn("error in SetWindowText: {}", .{win32.GetLastError()});
|
0,
|
||||||
return error.Win32;
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_background(color: u32) void {
|
pub fn updateScreen(screen: *const vaxis.Screen) void {
|
||||||
const hwnd = global.hwnd orelse return;
|
global.shared_screen.mutex.lock();
|
||||||
_ = win32.SendMessageW(hwnd, WM_APP_SET_BACKGROUND, color, 0);
|
defer global.shared_screen.mutex.unlock();
|
||||||
}
|
_ = global.shared_screen.arena.reset(.retain_capacity);
|
||||||
|
|
||||||
// returns false if there is no hwnd
|
const buf = global.shared_screen.arena.allocator().alloc(vaxis.Cell, screen.buf.len) catch |e| oom(e);
|
||||||
pub fn updateScreen(screen: *const vaxis.Screen) bool {
|
|
||||||
global.mutex.lock();
|
|
||||||
defer global.mutex.unlock();
|
|
||||||
|
|
||||||
const hwnd = global.hwnd orelse return false;
|
|
||||||
const state = stateFromHwnd(hwnd);
|
|
||||||
|
|
||||||
_ = state.shared_screen_arena.reset(.retain_capacity);
|
|
||||||
|
|
||||||
const buf = state.shared_screen_arena.allocator().alloc(vaxis.Cell, screen.buf.len) catch |e| oom(e);
|
|
||||||
@memcpy(buf, screen.buf);
|
@memcpy(buf, screen.buf);
|
||||||
for (buf) |*cell| {
|
for (buf) |*cell| {
|
||||||
cell.char.grapheme = state.shared_screen_arena.allocator().dupe(u8, cell.char.grapheme) catch |e| oom(e);
|
cell.char.grapheme = global.shared_screen.arena.allocator().dupe(
|
||||||
|
u8,
|
||||||
|
cell.char.grapheme,
|
||||||
|
) catch |e| oom(e);
|
||||||
}
|
}
|
||||||
state.shared_screen = .{
|
global.shared_screen.obj = .{
|
||||||
.width = screen.width,
|
.width = screen.width,
|
||||||
.height = screen.height,
|
.height = screen.height,
|
||||||
.width_pix = screen.width_pix,
|
.width_pix = screen.width_pix,
|
||||||
|
@ -540,8 +515,6 @@ pub fn updateScreen(screen: *const vaxis.Screen) bool {
|
||||||
.mouse_shape = screen.mouse_shape,
|
.mouse_shape = screen.mouse_shape,
|
||||||
.cursor_shape = undefined,
|
.cursor_shape = undefined,
|
||||||
};
|
};
|
||||||
win32.invalidateHwnd(hwnd);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: we round the text metric up to the nearest integer which
|
// NOTE: we round the text metric up to the nearest integer which
|
||||||
|
@ -1056,11 +1029,12 @@ fn WndProc(
|
||||||
state.currently_rendered_cell_size = getCellSize(text_format_editor);
|
state.currently_rendered_cell_size = getCellSize(text_format_editor);
|
||||||
|
|
||||||
{
|
{
|
||||||
global.mutex.lock();
|
global.shared_screen.mutex.lock();
|
||||||
defer global.mutex.unlock();
|
defer global.shared_screen.mutex.unlock();
|
||||||
paint(
|
paint(
|
||||||
&state.maybe_d2d.?,
|
&state.maybe_d2d.?,
|
||||||
&state.shared_screen,
|
RGB.from_u24(if (state.background) |b| @intCast(0xffffff & b) else 0),
|
||||||
|
&global.shared_screen.obj,
|
||||||
text_format_editor,
|
text_format_editor,
|
||||||
state.currently_rendered_cell_size.?,
|
state.currently_rendered_cell_size.?,
|
||||||
);
|
);
|
||||||
|
@ -1137,7 +1111,13 @@ fn WndProc(
|
||||||
},
|
},
|
||||||
WM_APP_EXIT => {
|
WM_APP_EXIT => {
|
||||||
win32.PostQuitMessage(0);
|
win32.PostQuitMessage(0);
|
||||||
return 0;
|
return WM_APP_EXIT_RESULT;
|
||||||
|
},
|
||||||
|
WM_APP_SET_BACKGROUND => {
|
||||||
|
const state = stateFromHwnd(hwnd);
|
||||||
|
state.background = @intCast(wparam);
|
||||||
|
win32.invalidateHwnd(hwnd);
|
||||||
|
return WM_APP_SET_BACKGROUND_RESULT;
|
||||||
},
|
},
|
||||||
win32.WM_CREATE => {
|
win32.WM_CREATE => {
|
||||||
const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam)));
|
const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam)));
|
||||||
|
@ -1146,7 +1126,6 @@ fn WndProc(
|
||||||
|
|
||||||
state.* = .{
|
state.* = .{
|
||||||
.pid = create_args.pid,
|
.pid = create_args.pid,
|
||||||
.shared_screen_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator),
|
|
||||||
};
|
};
|
||||||
const existing = win32.SetWindowLongPtrW(
|
const existing = win32.SetWindowLongPtrW(
|
||||||
hwnd,
|
hwnd,
|
||||||
|
@ -1159,10 +1138,9 @@ fn WndProc(
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
win32.WM_DESTROY => {
|
win32.WM_DESTROY => {
|
||||||
const state = stateFromHwnd(hwnd);
|
// the window should never be destroyed so as to not to invalidate
|
||||||
state.deinit();
|
// hwnd reference
|
||||||
// no need to free, it was allocated via an arena
|
@panic("gui window erroneously destroyed");
|
||||||
return 0;
|
|
||||||
},
|
},
|
||||||
else => return win32.DefWindowProcW(hwnd, msg, wparam, lparam),
|
else => return win32.DefWindowProcW(hwnd, msg, wparam, lparam),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue