Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions app/restart.zig
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ pub fn startedByHandover() bool {
return handed_from != null;
}

/// The new instance, up and hidden: tell the old one, which quits. The caller shows its window now,
/// over the old one's at the same place, so a window is on screen throughout; the lock is taken
/// once the old one has let go of it (`pollReleased`).
/// The new instance, its window on screen over the old one's at the same place: tell the old one,
/// which quits, so a window is on screen throughout; the lock is taken once the old one has let go
/// of it (`pollReleased`).
pub fn signalReady(io: std.Io) void {
if (comptime !supported) return;
const dir = handed_from orelse return;
Expand All @@ -269,6 +269,9 @@ var released_deadline_ns: i96 = 0;
pub fn pollReleased(io: std.Io, gpa: std.mem.Allocator) bool {
if (comptime !supported) return false;
const dir = handed_from orelse return false;
// Not yet ready (`signalReady`, once the window is on screen): the old instance still holds
// the lock, and has not been asked to let go of it.
if (released_deadline_ns == 0) return false;
var buf: [std.fs.max_path_bytes]u8 = undefined;
const released = std.fmt.bufPrint(&buf, "{s}/released", .{dir}) catch return false;
const done = if (std.Io.Dir.cwd().access(io, released, .{})) |_| true else |_| std.Io.Clock.awake.now(io).nanoseconds > released_deadline_ns;
Expand Down
34 changes: 34 additions & 0 deletions backend/src/platform/window.zig
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,40 @@ pub fn show(win: *dvui.Window, maximized: bool) void {
if (maximized) _ = c.SDL_MaximizeWindow(win.backend.impl.window);
}

/// Show the window without it being seen yet, so it can draw its first frame before anyone
/// looks (`reveal`). A hidden window gets nothing to present into, so it cannot draw first and show
/// after; shown but unseen, it can. On macOS the window is shown fully transparent (`alphaValue`);
/// on Windows it is cloaked (`DWMWA_CLOAK`), shown and composited but not on screen, which keeps it
/// out of layered windows (see `win32_titlebar`). Elsewhere it shows as it is, and `reveal` does
/// nothing.
pub fn showUndrawn(win: *dvui.Window, maximized: bool) void {
const window = win.backend.impl.window;
switch (builtin.os.tag) {
.macos => _ = c.SDL_SetWindowOpacity(window, 0),
.windows => setCloaked(win, true),
else => {},
}
show(win, maximized);
}

/// The first frame is on screen: let it be seen (`showUndrawn`).
pub fn reveal(win: *dvui.Window) void {
switch (builtin.os.tag) {
.macos => _ = c.SDL_SetWindowOpacity(win.backend.impl.window, 1),
.windows => setCloaked(win, false),
else => {},
}
}

fn setCloaked(win: *dvui.Window, cloaked: bool) void {
if (comptime builtin.os.tag != .windows) return;
const hwnd = win32_titlebar.getWin32Hwnd(win) orelse return;
const win32 = @import("win32");
const value: u32 = @intFromBool(cloaked);
// DWMWA_CLOAK.
_ = win32.graphics.dwm.DwmSetWindowAttribute(@ptrCast(hwnd), @as(win32.graphics.dwm.DWMWINDOWATTRIBUTE, @enumFromInt(13)), &value, @sizeOf(u32));
}

pub fn isMaximized(win: *dvui.Window) bool {
return windowMaximized(win.backend.impl.window);
}
Expand Down
58 changes: 33 additions & 25 deletions src/Entry.zig
Original file line number Diff line number Diff line change
Expand Up @@ -408,32 +408,39 @@ pub fn AppInit(win: *dvui.Window) !void {
// window animations.
fizzy.backend.macosLaunchComplete();

// Started by a restart's handover: up and hidden, the session read. The old instance goes
// now, and this window shows over its own at the same place (`restart`); the lock follows
// once the old one lets go of it (`Editor.tick`).
if (restart.startedByHandover()) {
restart.signalReady(dvui.io);
fizzy.core.profile.phase(dvui.io, "handover ready");
}

// Chrome and geometry are settled — reveal the window (created hidden).
fizzy.backend.showWindow(win);
fizzy.core.profile.phase(dvui.io, "shown");
}

/// The first frame has ended: the app is up. Says how long that took, once.
var first_frame_done = false;
fn noteFirstFrame() void {
if (first_frame_done) return;
first_frame_done = true;
fizzy.core.profile.phase(dvui.io, "first frame");
const ph = fizzy.core.profile.phases() orelse return;
std.log.info("startup: up {d:.0} ms after main (window shown at {d:.0} ms)", .{ ph.ms(ph.len - 1), shownMs(ph) });
// Chrome and geometry are settled: the window shows, but unseen until its first frame has
// reached the screen (`revealOnceDrawn`), so it never appears as an empty window first.
fizzy.backend.showWindowUndrawn(win);
}

fn shownMs(ph: *const fizzy.core.profile.Phases) f64 {
for (0..ph.len) |i| if (std.mem.eql(u8, ph.names[i], "shown")) return ph.ms(i);
return 0;
/// Where the window's reveal is (`revealOnceDrawn`): drawing its first frame, then seen.
var reveal: enum { first_frame, next_frame, done } = .first_frame;

/// At each frame's start, then its end: the first frame is drawn into the unseen window and
/// presented as the frame ends, so the window is revealed as the next one begins, which the
/// first asks for. Then a restart's handover tells the old instance this one is ready, now that it
/// is on screen over the old one's window at the same place (`restart`); the lock follows once
/// the old one lets go of it (`Editor.tick`).
fn revealOnceDrawn(at_end: bool) void {
switch (reveal) {
.first_frame => if (at_end) {
fizzy.core.profile.phase(dvui.io, "first frame");
reveal = .next_frame;
dvui.refresh(null, @src(), null);
},
.next_frame => if (!at_end) {
fizzy.backend.revealWindow(fizzy.entry().window);
fizzy.core.profile.phase(dvui.io, "shown");
reveal = .done;
if (restart.startedByHandover()) {
restart.signalReady(dvui.io);
fizzy.core.profile.phase(dvui.io, "handover ready");
}
const ph = fizzy.core.profile.phases() orelse return;
std.log.info("startup: shown {d:.0} ms after main", .{ph.ms(ph.len - 1)});
},
.done => {},
}
}

// Run as app is shutting down before dvui.Window.deinit()
Expand Down Expand Up @@ -468,7 +475,8 @@ pub fn AppFrame() !dvui.App.Result {
_ = dvui.debug.logRefresh(refresh_log_active or fizzy.core.profile.recordingCauses());
fizzy.core.profile.hostFrameBegin(lastSubmitNs());
defer fizzy.core.profile.hostFrameEnd();
defer noteFirstFrame();
revealOnceDrawn(false);
defer revealOnceDrawn(true);
singleton.drainPending();
// Once, or — while a demo is seeking — again and again unseen until it lands (`frames`).
const player = &fizzy.editor().demo.player;
Expand Down
8 changes: 8 additions & 0 deletions src/backend/backend_native.zig
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ pub fn showWindow(win: *dvui.Window) void {
platform.window.show(win, platform.geometry.restoredMaximized());
}

/// Show the window unseen, to draw its first frame before it is seen (`revealWindow`).
pub fn showWindowUndrawn(win: *dvui.Window) void {
platform.window.showUndrawn(win, platform.geometry.restoredMaximized());
}

/// Its first frame is on screen: let it be seen.
pub const revealWindow = platform.window.reveal;

/// Style the window (macOS: frame == content first), put it back where it was left
/// (`platform.geometry`, kept in fizzy's `layout.zon`), and follow it through Spaces, zooms and
/// live resizes (`platform.macos_monitor`). Called from `AppInit` while the window is still hidden:
Expand Down
2 changes: 2 additions & 0 deletions src/backend/backend_web.zig
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ pub fn restoreWindowState(_: *dvui.Window) void {}

/// Symmetric with the native API: the web canvas is always visible.
pub fn showWindow(_: *dvui.Window) void {}
pub fn showWindowUndrawn(_: *dvui.Window) void {}
pub fn revealWindow(_: *dvui.Window) void {}

/// Symmetric with the native API: no window geometry to persist on web.
pub fn saveWindowGeometry(_: *dvui.Window) void {}
Expand Down
Loading