From e84489c3457b07ed94482ddb266eefe7c2d3957c Mon Sep 17 00:00:00 2001 From: alex Date: Mon, 7 Aug 2023 22:53:55 +0200 Subject: [PATCH] ui: keep last comm reports to update on wakeup a previous commit a06a4757 stopped updating the UI while in standby mode. unfortunately, this makes the UI data become stale on wakeup. for example, bitcoin chain height and its timestamp. this commit keeps the last report received from daemon comm during standby and uses it to update the UI immediately on wakeup. --- src/ngui.zig | 78 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/src/ngui.zig b/src/ngui.zig index 6f8936d..fe771da 100644 --- a/src/ngui.zig +++ b/src/ngui.zig @@ -33,12 +33,56 @@ var gpa: std.mem.Allocator = undefined; /// all nm_xxx functions assume it is the case since they are invoked from lvgl c code. var ui_mutex: std.Thread.Mutex = .{}; +/// current state of the GUI. +/// guarded by ui_mutex since some nm_xxx funcs branch based off of the state. var state: enum { active, // normal operational mode standby, // idling alert, // draw user attention; never go standby } = .active; +/// last report received from comm. +/// deinit'ed at program exit. +/// while deinit and replace handle concurrency, field access requires holding mu. +var last_report: struct { + mu: std.Thread.Mutex = .{}, + network: ?comm.Message.NetworkReport = null, + bitcoind: ?comm.Message.BitcoindReport = null, + + fn deinit(self: *@This()) void { + self.mu.lock(); + defer self.mu.unlock(); + if (self.network) |v| { + comm.free(gpa, .{ .network_report = v }); + self.network = null; + } + if (self.bitcoind) |v| { + comm.free(gpa, .{ .bitcoind_report = v }); + self.bitcoind = null; + } + } + + fn replace(self: *@This(), new: anytype) void { + self.mu.lock(); + defer self.mu.unlock(); + switch (@TypeOf(new)) { + comm.Message.NetworkReport => { + if (self.network) |old| { + comm.free(gpa, .{ .network_report = old }); + } + self.network = new; + }, + comm.Message.BitcoindReport => { + if (self.bitcoind) |old| { + comm.free(gpa, .{ .bitcoind_report = old }); + } + self.bitcoind = new; + }, + else => @compileError("unhandled type: " ++ @typeName(@TypeOf(new))), + } + } +} = .{}; + /// the program runs until sigquit is true. /// set from sighandler or on unrecoverable comm failure with the daemon. var sigquit: std.Thread.ResetEvent = .{}; @@ -175,27 +219,29 @@ fn commThreadLoop() void { /// the UI accordingly. /// holds ui mutex for most of the duration. fn commThreadLoopCycle() !void { - const msg = try comm.read(gpa, stdin); - defer comm.free(gpa, msg); - logger.debug("got msg: {s}", .{@tagName(msg)}); - - ui_mutex.lock(); // guards state and all UI calls below + const msg = try comm.read(gpa, stdin); // blocking + ui_mutex.lock(); // guards the state and all UI calls below defer ui_mutex.unlock(); switch (state) { .standby => switch (msg) { .ping => try comm.write(gpa, stdout, comm.Message.pong), - else => logger.debug("ignoring: in standby", .{}), + .network_report => |v| last_report.replace(v), + .bitcoind_report => |v| last_report.replace(v), + else => logger.debug("ignoring {s}: in standby", .{@tagName(msg)}), }, .active, .alert => switch (msg) { .ping => try comm.write(gpa, stdout, comm.Message.pong), - .network_report => |report| { - updateNetworkStatus(report) catch |err| logger.err("updateNetworkStatus: {any}", .{err}); + .poweroff_progress => |rep| { + ui.poweroff.updateStatus(rep) catch |err| logger.err("poweroff.updateStatus: {any}", .{err}); + comm.free(gpa, msg); }, - .poweroff_progress => |report| { - ui.poweroff.updateStatus(report) catch |err| logger.err("poweroff.updateStatus: {any}", .{err}); + .network_report => |rep| { + updateNetworkStatus(rep) catch |err| logger.err("updateNetworkStatus: {any}", .{err}); + last_report.replace(rep); }, .bitcoind_report => |rep| { ui.bitcoin.updateTabPanel(rep) catch |err| logger.err("bitcoin.updateTabPanel: {any}", .{err}); + last_report.replace(rep); }, else => logger.warn("unhandled msg tag {s}", .{@tagName(msg)}), }, @@ -225,14 +271,23 @@ fn uiThreadLoop() void { // wake up due to touch screen activity or wakeup event is set logger.info("waking up from sleep", .{}); ui_mutex.lock(); + defer ui_mutex.unlock(); if (state == .standby) { state = .active; comm.write(gpa, stdout, comm.Message.wakeup) catch |err| { logger.err("comm.write wakeup: {any}", .{err}); }; lvgl.resetIdle(); + + last_report.mu.lock(); + defer last_report.mu.unlock(); + if (last_report.network) |rep| { + updateNetworkStatus(rep) catch |err| logger.err("updateNetworkStatus: {any}", .{err}); + } + if (last_report.bitcoind) |rep| { + ui.bitcoin.updateTabPanel(rep) catch |err| logger.err("bitcoin.updateTabPanel: {any}", .{err}); + } } - ui_mutex.unlock(); continue; }, } @@ -331,6 +386,7 @@ pub fn main() anyerror!void { try os.sigaction(os.SIG.TERM, &sa, null); sigquit.wait(); + last_report.deinit(); logger.info("main terminated", .{}); }