win32 gui: more fixes/edge cases
This commit is contained in:
		
							parent
							
								
									87a355b854
								
							
						
					
					
						commit
						fd86653db1
					
				
					 1 changed files with 54 additions and 53 deletions
				
			
		|  | @ -47,12 +47,17 @@ fn onexit(e: error{Exit}) void { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const global = struct { | const global = struct { | ||||||
|  |     var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | ||||||
|  |     const arena = arena_instance.allocator(); | ||||||
|     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 state: ?State = null; | ||||||
|     var d2d_factory: *win32.ID2D1Factory = undefined; |     var d2d_factory: *win32.ID2D1Factory = undefined; | ||||||
|     var conf: ?*gui_config = null; |     var conf: ?gui_config = null; | ||||||
|  |     var fontface: ?[:0]const u16 = null; | ||||||
|  | 
 | ||||||
|     var text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{}; |     var text_format_editor: ddui.TextFormatCache(Dpi, createTextFormatEditor) = .{}; | ||||||
| 
 | 
 | ||||||
|     const shared_screen = struct { |     const shared_screen = struct { | ||||||
|  | @ -150,27 +155,41 @@ const Dpi = struct { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | fn getConfig() *const gui_config { | ||||||
|  |     if (global.conf == null) { | ||||||
|  |         global.conf, _ = root.read_config(gui_config, global.arena); | ||||||
|  |         root.write_config(global.conf.?, global.arena) catch | ||||||
|  |             std.log.err("failed to write gui config file", .{}); | ||||||
|  |     } | ||||||
|  |     return &global.conf.?; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn getFieldDefault(field: std.builtin.Type.StructField) ?*const field.type { | ||||||
|  |     return @alignCast(@ptrCast(field.default_value orelse return null)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn getFontFace() [:0]const u16 { | ||||||
|  |     if (global.fontface == null) { | ||||||
|  |         const conf = getConfig(); | ||||||
|  |         global.fontface = blk: { | ||||||
|  |             break :blk std.unicode.utf8ToUtf16LeAllocZ(global.arena, conf.fontface) catch |e| { | ||||||
|  |                 std.log.err("failed to convert fontface name with {s}", .{@errorName(e)}); | ||||||
|  |                 const default = comptime getFieldDefault( | ||||||
|  |                     std.meta.fieldInfo(gui_config, .fontface), | ||||||
|  |                 ) orelse @compileError("fontface is missing default"); | ||||||
|  |                 break :blk win32.L(default.*); | ||||||
|  |             }; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |     return global.fontface.?; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| fn createTextFormatEditor(dpi: Dpi) *win32.IDWriteTextFormat { | fn createTextFormatEditor(dpi: Dpi) *win32.IDWriteTextFormat { | ||||||
|     const default_config = gui_config{}; |     const conf = getConfig(); | ||||||
|     const fontface_utf8 = if (global.conf) |conf| conf.fontface else blk: { |  | ||||||
|         std.log.err("global gui config not found", .{}); |  | ||||||
|         break :blk default_config.fontface; |  | ||||||
|     }; |  | ||||||
|     const fontsize = if (global.conf) |conf| conf.fontsize else default_config.fontsize; |  | ||||||
| 
 |  | ||||||
|     var buf: [4096]u8 = undefined; |  | ||||||
|     var fba = std.heap.FixedBufferAllocator.init(&buf); |  | ||||||
|     var fontface = std.ArrayList(u16).init(fba.allocator()); |  | ||||||
|     std.unicode.utf8ToUtf16LeArrayList(&fontface, fontface_utf8) catch { |  | ||||||
|         std.log.err("fontface contains invalid UTF-8", .{}); |  | ||||||
|         fontface.clearRetainingCapacity(); |  | ||||||
|         std.unicode.utf8ToUtf16LeArrayList(&fontface, default_config.fontface) catch {}; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     var err: HResultError = undefined; |     var err: HResultError = undefined; | ||||||
|     return ddui.createTextFormat(global.dwrite_factory, &err, .{ |     return ddui.createTextFormat(global.dwrite_factory, &err, .{ | ||||||
|         .size = win32.scaleDpi(f32, @as(f32, @floatFromInt(fontsize)), dpi.value), |         .size = win32.scaleDpi(f32, @as(f32, @floatFromInt(conf.fontsize)), dpi.value), | ||||||
|         .family_name = fontface.toOwnedSliceSentinel(0) catch @panic("OOM:createTextFormatEditor"), |         .family_name = getFontFace(), | ||||||
|     }) catch std.debug.panic("{s} failed, hresult=0x{x}", .{ err.context, err.hr }); |     }) catch std.debug.panic("{s} failed, hresult=0x{x}", .{ err.context, err.hr }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -241,19 +260,18 @@ const D2d = struct { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const State = struct { | const State = struct { | ||||||
|  |     hwnd: win32.HWND, | ||||||
|     pid: thespian.pid, |     pid: thespian.pid, | ||||||
|     maybe_d2d: ?D2d = null, |     maybe_d2d: ?D2d = null, | ||||||
|     erase_bg_done: bool = false, |     erase_bg_done: bool = false, | ||||||
|     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, |     background: ?u32 = null, | ||||||
|     conf: gui_config, |  | ||||||
|     last_sizing_edge: ?win32.WPARAM = null, |     last_sizing_edge: ?win32.WPARAM = null, | ||||||
| }; | }; | ||||||
| fn stateFromHwnd(hwnd: win32.HWND) *State { | fn stateFromHwnd(hwnd: win32.HWND) *State { | ||||||
|     const addr: usize = @bitCast(win32.GetWindowLongPtrW(hwnd, @enumFromInt(0))); |     std.debug.assert(hwnd == global.state.?.hwnd); | ||||||
|     if (addr == 0) @panic("window is missing it's state!"); |     return &global.state.?; | ||||||
|     return @ptrFromInt(addr); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn paint( | fn paint( | ||||||
|  | @ -396,9 +414,7 @@ fn calcWindowPlacement( | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const CreateWindowArgs = struct { | const CreateWindowArgs = struct { | ||||||
|     allocator: std.mem.Allocator, |  | ||||||
|     pid: thespian.pid, |     pid: thespian.pid, | ||||||
|     conf: gui_config, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub fn start() !std.Thread { | pub fn start() !std.Thread { | ||||||
|  | @ -408,18 +424,14 @@ pub fn start() !std.Thread { | ||||||
|     return try std.Thread.spawn(.{}, entry, .{pid}); |     return try std.Thread.spawn(.{}, entry, .{pid}); | ||||||
| } | } | ||||||
| fn entry(pid: thespian.pid) !void { | fn entry(pid: thespian.pid) !void { | ||||||
|     var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); |     const conf = getConfig(); | ||||||
|     defer arena_instance.deinit(); |  | ||||||
| 
 |  | ||||||
|     const CLASS_NAME = win32.L("Flow"); |  | ||||||
| 
 |  | ||||||
|     const conf, _ = root.read_config(gui_config, arena_instance.allocator()); |  | ||||||
|     root.write_config(conf, arena_instance.allocator()) catch |  | ||||||
|         std.log.err("failed to write gui config file", .{}); |  | ||||||
| 
 | 
 | ||||||
|     const maybe_monitor: ?win32.HMONITOR = blk: { |     const maybe_monitor: ?win32.HMONITOR = blk: { | ||||||
|         break :blk win32.MonitorFromPoint( |         break :blk win32.MonitorFromPoint( | ||||||
|             .{ .x = 0, .y = 0 }, |             .{ | ||||||
|  |                 .x = conf.initial_window_x, | ||||||
|  |                 .y = conf.initial_window_y, | ||||||
|  |             }, | ||||||
|             win32.MONITOR_DEFAULTTOPRIMARY, |             win32.MONITOR_DEFAULTTOPRIMARY, | ||||||
|         ) orelse { |         ) orelse { | ||||||
|             std.log.warn("MonitorFromPoint failed with {}", .{win32.GetLastError().fmt()}); |             std.log.warn("MonitorFromPoint failed with {}", .{win32.GetLastError().fmt()}); | ||||||
|  | @ -455,13 +467,15 @@ fn entry(pid: thespian.pid) !void { | ||||||
|     ); |     ); | ||||||
|     global.icons = getIcons(initial_placement.dpi); |     global.icons = getIcons(initial_placement.dpi); | ||||||
| 
 | 
 | ||||||
|  |     const CLASS_NAME = win32.L("Flow"); | ||||||
|  | 
 | ||||||
|     // we only need to register the window class once per process |     // we only need to register the window class once per process | ||||||
|     const wc = win32.WNDCLASSEXW{ |     const wc = win32.WNDCLASSEXW{ | ||||||
|         .cbSize = @sizeOf(win32.WNDCLASSEXW), |         .cbSize = @sizeOf(win32.WNDCLASSEXW), | ||||||
|         .style = .{}, |         .style = .{}, | ||||||
|         .lpfnWndProc = WndProc, |         .lpfnWndProc = WndProc, | ||||||
|         .cbClsExtra = 0, |         .cbClsExtra = 0, | ||||||
|         .cbWndExtra = @sizeOf(*State), |         .cbWndExtra = 0, | ||||||
|         .hInstance = win32.GetModuleHandleW(null), |         .hInstance = win32.GetModuleHandleW(null), | ||||||
|         .hIcon = global.icons.large, |         .hIcon = global.icons.large, | ||||||
|         .hCursor = win32.LoadCursorW(null, win32.IDC_ARROW), |         .hCursor = win32.LoadCursorW(null, win32.IDC_ARROW), | ||||||
|  | @ -475,11 +489,7 @@ fn entry(pid: thespian.pid) !void { | ||||||
|         win32.GetLastError(), |         win32.GetLastError(), | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     var create_args = CreateWindowArgs{ |     var create_args = CreateWindowArgs{ .pid = pid }; | ||||||
|         .allocator = arena_instance.allocator(), |  | ||||||
|         .pid = pid, |  | ||||||
|         .conf = conf, |  | ||||||
|     }; |  | ||||||
|     const hwnd = win32.CreateWindowExW( |     const hwnd = win32.CreateWindowExW( | ||||||
|         window_style_ex, |         window_style_ex, | ||||||
|         CLASS_NAME, // Window class |         CLASS_NAME, // Window class | ||||||
|  | @ -1226,23 +1236,14 @@ fn WndProc( | ||||||
|             return WM_APP_SET_BACKGROUND_RESULT; |             return WM_APP_SET_BACKGROUND_RESULT; | ||||||
|         }, |         }, | ||||||
|         win32.WM_CREATE => { |         win32.WM_CREATE => { | ||||||
|  |             std.debug.assert(global.state == null); | ||||||
|             const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam))); |             const create_struct: *win32.CREATESTRUCTW = @ptrFromInt(@as(usize, @bitCast(lparam))); | ||||||
|             const create_args: *CreateWindowArgs = @alignCast(@ptrCast(create_struct.lpCreateParams)); |             const create_args: *CreateWindowArgs = @alignCast(@ptrCast(create_struct.lpCreateParams)); | ||||||
|             const state = create_args.allocator.create(State) catch |e| oom(e); |             global.state = .{ | ||||||
| 
 |                 .hwnd = hwnd, | ||||||
|             state.* = .{ |  | ||||||
|                 .pid = create_args.pid, |                 .pid = create_args.pid, | ||||||
|                 .conf = create_args.conf, |  | ||||||
|             }; |             }; | ||||||
|             global.conf = &state.conf; |             std.debug.assert(&(global.state.?) == stateFromHwnd(hwnd)); | ||||||
| 
 |  | ||||||
|             const existing = win32.SetWindowLongPtrW( |  | ||||||
|                 hwnd, |  | ||||||
|                 @enumFromInt(0), |  | ||||||
|                 @as(isize, @bitCast(@intFromPtr(state))), |  | ||||||
|             ); |  | ||||||
|             std.debug.assert(existing == 0); |  | ||||||
|             std.debug.assert(state == stateFromHwnd(hwnd)); |  | ||||||
|             sendResize(hwnd); |             sendResize(hwnd); | ||||||
|             return 0; |             return 0; | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jonathan Marler
						Jonathan Marler