ui: keep last comm reports to update on wakeup
ci/woodpecker/push/woodpecker Pipeline was successful Details
ci/woodpecker/tag/woodpecker Pipeline was successful Details

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.
pull/26/head v0.3.1
alex 1 year ago
parent a06a4757b2
commit e84489c345
Signed by: x1ddos
GPG Key ID: FDEFB4A63CBD8460

@ -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. /// all nm_xxx functions assume it is the case since they are invoked from lvgl c code.
var ui_mutex: std.Thread.Mutex = .{}; 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 { var state: enum {
active, // normal operational mode active, // normal operational mode
standby, // idling standby, // idling
alert, // draw user attention; never go standby alert, // draw user attention; never go standby
} = .active; } = .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. /// the program runs until sigquit is true.
/// set from sighandler or on unrecoverable comm failure with the daemon. /// set from sighandler or on unrecoverable comm failure with the daemon.
var sigquit: std.Thread.ResetEvent = .{}; var sigquit: std.Thread.ResetEvent = .{};
@ -175,27 +219,29 @@ fn commThreadLoop() void {
/// the UI accordingly. /// the UI accordingly.
/// holds ui mutex for most of the duration. /// holds ui mutex for most of the duration.
fn commThreadLoopCycle() !void { fn commThreadLoopCycle() !void {
const msg = try comm.read(gpa, stdin); const msg = try comm.read(gpa, stdin); // blocking
defer comm.free(gpa, msg); ui_mutex.lock(); // guards the state and all UI calls below
logger.debug("got msg: {s}", .{@tagName(msg)});
ui_mutex.lock(); // guards state and all UI calls below
defer ui_mutex.unlock(); defer ui_mutex.unlock();
switch (state) { switch (state) {
.standby => switch (msg) { .standby => switch (msg) {
.ping => try comm.write(gpa, stdout, comm.Message.pong), .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) { .active, .alert => switch (msg) {
.ping => try comm.write(gpa, stdout, comm.Message.pong), .ping => try comm.write(gpa, stdout, comm.Message.pong),
.network_report => |report| { .poweroff_progress => |rep| {
updateNetworkStatus(report) catch |err| logger.err("updateNetworkStatus: {any}", .{err}); ui.poweroff.updateStatus(rep) catch |err| logger.err("poweroff.updateStatus: {any}", .{err});
comm.free(gpa, msg);
}, },
.poweroff_progress => |report| { .network_report => |rep| {
ui.poweroff.updateStatus(report) catch |err| logger.err("poweroff.updateStatus: {any}", .{err}); updateNetworkStatus(rep) catch |err| logger.err("updateNetworkStatus: {any}", .{err});
last_report.replace(rep);
}, },
.bitcoind_report => |rep| { .bitcoind_report => |rep| {
ui.bitcoin.updateTabPanel(rep) catch |err| logger.err("bitcoin.updateTabPanel: {any}", .{err}); 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)}), 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 // wake up due to touch screen activity or wakeup event is set
logger.info("waking up from sleep", .{}); logger.info("waking up from sleep", .{});
ui_mutex.lock(); ui_mutex.lock();
defer ui_mutex.unlock();
if (state == .standby) { if (state == .standby) {
state = .active; state = .active;
comm.write(gpa, stdout, comm.Message.wakeup) catch |err| { comm.write(gpa, stdout, comm.Message.wakeup) catch |err| {
logger.err("comm.write wakeup: {any}", .{err}); logger.err("comm.write wakeup: {any}", .{err});
}; };
lvgl.resetIdle(); 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; continue;
}, },
} }
@ -331,6 +386,7 @@ pub fn main() anyerror!void {
try os.sigaction(os.SIG.TERM, &sa, null); try os.sigaction(os.SIG.TERM, &sa, null);
sigquit.wait(); sigquit.wait();
last_report.deinit();
logger.info("main terminated", .{}); logger.info("main terminated", .{});
} }