fix: fix periodic freeze in print stack
This commit is contained in:
@@ -188,6 +188,34 @@ inline void sanitize_field(char* s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool read_text_file_into_buffer(const char* path, char* out, int out_len) {
|
||||||
|
if (path == nullptr || out == nullptr || out_len <= 0) return false;
|
||||||
|
FILE* f = fopen(path, "rb");
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
|
size_t n = fread(out, 1, (size_t)out_len - 1, f);
|
||||||
|
fclose(f);
|
||||||
|
out[n] = '\0';
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
inline void parse_key_value_lines(char* text, Fn fn) {
|
||||||
|
if (text == nullptr) return;
|
||||||
|
char* cursor = text;
|
||||||
|
while (*cursor) {
|
||||||
|
char* line = cursor;
|
||||||
|
while (*cursor && *cursor != '\n') cursor++;
|
||||||
|
if (*cursor == '\n') *cursor++ = '\0';
|
||||||
|
|
||||||
|
trim_line(line);
|
||||||
|
char* eq = strchr(line, '=');
|
||||||
|
if (!eq) continue;
|
||||||
|
*eq++ = '\0';
|
||||||
|
fn(line, eq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void now_string(char* out, int out_len) {
|
inline void now_string(char* out, int out_len) {
|
||||||
if (out == nullptr || out_len <= 0) return;
|
if (out == nullptr || out_len <= 0) return;
|
||||||
Montauk::DateTime dt = {};
|
Montauk::DateTime dt = {};
|
||||||
@@ -264,13 +292,15 @@ inline void make_doc_file_path(const char* id, char* out, int out_len) {
|
|||||||
|
|
||||||
inline bool read_text_file_line(const char* path, char* out, int out_len) {
|
inline bool read_text_file_line(const char* path, char* out, int out_len) {
|
||||||
if (path == nullptr || out == nullptr || out_len <= 0) return false;
|
if (path == nullptr || out == nullptr || out_len <= 0) return false;
|
||||||
FILE* f = fopen(path, "rb");
|
if (!read_text_file_into_buffer(path, out, out_len)) return false;
|
||||||
if (!f) return false;
|
for (int i = 0; out[i] != '\0'; i++) {
|
||||||
out[0] = '\0';
|
if (out[i] == '\r' || out[i] == '\n') {
|
||||||
bool ok = fgets(out, out_len, f) != nullptr;
|
out[i] = '\0';
|
||||||
fclose(f);
|
break;
|
||||||
if (ok) trim_line(out);
|
}
|
||||||
return ok;
|
}
|
||||||
|
trim_line(out);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool write_text_file_atomic(const char* path, const char* text) {
|
inline bool write_text_file_atomic(const char* path, const char* text) {
|
||||||
@@ -430,23 +460,15 @@ inline bool load_known_printer_from_path(const char* path, KnownPrinter* printer
|
|||||||
if (path == nullptr || printer == nullptr) return false;
|
if (path == nullptr || printer == nullptr) return false;
|
||||||
zero_known_printer(printer);
|
zero_known_printer(printer);
|
||||||
|
|
||||||
FILE* f = fopen(path, "rb");
|
char text[1024];
|
||||||
if (!f) return false;
|
if (!read_text_file_into_buffer(path, text, sizeof(text))) return false;
|
||||||
|
parse_key_value_lines(text, [&](const char* line, const char* eq) {
|
||||||
char line[384];
|
|
||||||
while (fgets(line, sizeof(line), f) != nullptr) {
|
|
||||||
trim_line(line);
|
|
||||||
char* eq = strchr(line, '=');
|
|
||||||
if (!eq) continue;
|
|
||||||
*eq++ = '\0';
|
|
||||||
|
|
||||||
if (strcmp(line, "uri") == 0) safe_copy(printer->uri, sizeof(printer->uri), eq);
|
if (strcmp(line, "uri") == 0) safe_copy(printer->uri, sizeof(printer->uri), eq);
|
||||||
else if (strcmp(line, "display_name") == 0) safe_copy(printer->display_name, sizeof(printer->display_name), eq);
|
else if (strcmp(line, "display_name") == 0) safe_copy(printer->display_name, sizeof(printer->display_name), eq);
|
||||||
else if (strcmp(line, "last_seen") == 0) safe_copy(printer->last_seen, sizeof(printer->last_seen), eq);
|
else if (strcmp(line, "last_seen") == 0) safe_copy(printer->last_seen, sizeof(printer->last_seen), eq);
|
||||||
else if (strcmp(line, "status_message") == 0) safe_copy(printer->status_message, sizeof(printer->status_message), eq);
|
else if (strcmp(line, "status_message") == 0) safe_copy(printer->status_message, sizeof(printer->status_message), eq);
|
||||||
else if (strcmp(line, "ok") == 0) printer->ok = atoi(eq) != 0;
|
else if (strcmp(line, "ok") == 0) printer->ok = atoi(eq) != 0;
|
||||||
}
|
});
|
||||||
fclose(f);
|
|
||||||
return printer->uri[0] != '\0';
|
return printer->uri[0] != '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,16 +848,9 @@ inline bool load_job_from_path(const char* path, JobMeta* job) {
|
|||||||
if (path == nullptr || job == nullptr) return false;
|
if (path == nullptr || job == nullptr) return false;
|
||||||
zero_job(job);
|
zero_job(job);
|
||||||
|
|
||||||
FILE* f = fopen(path, "rb");
|
char text[4096];
|
||||||
if (!f) return false;
|
if (!read_text_file_into_buffer(path, text, sizeof(text))) return false;
|
||||||
|
parse_key_value_lines(text, [&](const char* line, const char* eq) {
|
||||||
char line[384];
|
|
||||||
while (fgets(line, sizeof(line), f) != nullptr) {
|
|
||||||
trim_line(line);
|
|
||||||
char* eq = strchr(line, '=');
|
|
||||||
if (!eq) continue;
|
|
||||||
*eq++ = '\0';
|
|
||||||
|
|
||||||
if (strcmp(line, "id") == 0) safe_copy(job->id, sizeof(job->id), eq);
|
if (strcmp(line, "id") == 0) safe_copy(job->id, sizeof(job->id), eq);
|
||||||
else if (strcmp(line, "state") == 0) safe_copy(job->state, sizeof(job->state), eq);
|
else if (strcmp(line, "state") == 0) safe_copy(job->state, sizeof(job->state), eq);
|
||||||
else if (strcmp(line, "source_kind") == 0) safe_copy(job->source_kind, sizeof(job->source_kind), eq);
|
else if (strcmp(line, "source_kind") == 0) safe_copy(job->source_kind, sizeof(job->source_kind), eq);
|
||||||
@@ -852,8 +867,7 @@ inline bool load_job_from_path(const char* path, JobMeta* job) {
|
|||||||
else if (strcmp(line, "remote_job_id") == 0) job->remote_job_id = atoi(eq);
|
else if (strcmp(line, "remote_job_id") == 0) job->remote_job_id = atoi(eq);
|
||||||
else if (strcmp(line, "size_bytes") == 0) job->size_bytes = atoi(eq);
|
else if (strcmp(line, "size_bytes") == 0) job->size_bytes = atoi(eq);
|
||||||
else if (strcmp(line, "copies") == 0) job->copies = atoi(eq);
|
else if (strcmp(line, "copies") == 0) job->copies = atoi(eq);
|
||||||
}
|
});
|
||||||
fclose(f);
|
|
||||||
if (job->copies <= 0) job->copies = 1;
|
if (job->copies <= 0) job->copies = 1;
|
||||||
return job->id[0] != '\0';
|
return job->id[0] != '\0';
|
||||||
}
|
}
|
||||||
@@ -914,16 +928,9 @@ inline bool load_probe_state(const char* path, ProbeState* state) {
|
|||||||
if (path == nullptr || state == nullptr) return false;
|
if (path == nullptr || state == nullptr) return false;
|
||||||
zero_probe_state(state);
|
zero_probe_state(state);
|
||||||
|
|
||||||
FILE* f = fopen(path, "rb");
|
char text[4096];
|
||||||
if (!f) return false;
|
if (!read_text_file_into_buffer(path, text, sizeof(text))) return false;
|
||||||
|
parse_key_value_lines(text, [&](const char* line, const char* eq) {
|
||||||
char line[512];
|
|
||||||
while (fgets(line, sizeof(line), f) != nullptr) {
|
|
||||||
trim_line(line);
|
|
||||||
char* eq = strchr(line, '=');
|
|
||||||
if (!eq) continue;
|
|
||||||
*eq++ = '\0';
|
|
||||||
|
|
||||||
if (strcmp(line, "ok") == 0) state->ok = atoi(eq) != 0;
|
if (strcmp(line, "ok") == 0) state->ok = atoi(eq) != 0;
|
||||||
else if (strcmp(line, "printer_uri") == 0) safe_copy(state->printer_uri, sizeof(state->printer_uri), eq);
|
else if (strcmp(line, "printer_uri") == 0) safe_copy(state->printer_uri, sizeof(state->printer_uri), eq);
|
||||||
else if (strcmp(line, "probed_at") == 0) safe_copy(state->probed_at, sizeof(state->probed_at), eq);
|
else if (strcmp(line, "probed_at") == 0) safe_copy(state->probed_at, sizeof(state->probed_at), eq);
|
||||||
@@ -946,8 +953,7 @@ inline bool load_probe_state(const char* path, ProbeState* state) {
|
|||||||
else if (strcmp(line, "caps_supported_formats") == 0) safe_copy(state->caps.supported_formats, sizeof(state->caps.supported_formats), eq);
|
else if (strcmp(line, "caps_supported_formats") == 0) safe_copy(state->caps.supported_formats, sizeof(state->caps.supported_formats), eq);
|
||||||
else if (strcmp(line, "caps_default_format") == 0) safe_copy(state->caps.default_format, sizeof(state->caps.default_format), eq);
|
else if (strcmp(line, "caps_default_format") == 0) safe_copy(state->caps.default_format, sizeof(state->caps.default_format), eq);
|
||||||
else if (strcmp(line, "caps_preferred_format") == 0) safe_copy(state->caps.preferred_format, sizeof(state->caps.preferred_format), eq);
|
else if (strcmp(line, "caps_preferred_format") == 0) safe_copy(state->caps.preferred_format, sizeof(state->caps.preferred_format), eq);
|
||||||
}
|
});
|
||||||
fclose(f);
|
|
||||||
return state->printer_uri[0] != '\0' || state->message[0] != '\0';
|
return state->printer_uri[0] != '\0' || state->message[0] != '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ extern "C" {
|
|||||||
using namespace print;
|
using namespace print;
|
||||||
|
|
||||||
static constexpr bool ENABLE_BACKGROUND_PROBE = false;
|
static constexpr bool ENABLE_BACKGROUND_PROBE = false;
|
||||||
|
static constexpr uint64_t DAEMON_STATE_HEARTBEAT_MS = 15000;
|
||||||
|
|
||||||
|
static char g_last_state_phase[32] = {};
|
||||||
|
static char g_last_state_subject[128] = {};
|
||||||
|
static char g_last_state_detail[256] = {};
|
||||||
|
static uint64_t g_last_state_write_ms = 0;
|
||||||
|
|
||||||
enum class ClaimResult {
|
enum class ClaimResult {
|
||||||
None,
|
None,
|
||||||
@@ -25,6 +31,23 @@ enum class ClaimResult {
|
|||||||
|
|
||||||
static void write_daemon_state(const char* phase, const char* subject = nullptr,
|
static void write_daemon_state(const char* phase, const char* subject = nullptr,
|
||||||
const char* detail = nullptr) {
|
const char* detail = nullptr) {
|
||||||
|
const char* safe_phase = phase ? phase : "";
|
||||||
|
const char* safe_subject = subject ? subject : "";
|
||||||
|
const char* safe_detail = detail ? detail : "";
|
||||||
|
uint64_t now_ms = montauk::get_milliseconds();
|
||||||
|
|
||||||
|
if (g_last_state_write_ms != 0
|
||||||
|
&& strcmp(g_last_state_phase, safe_phase) == 0
|
||||||
|
&& strcmp(g_last_state_subject, safe_subject) == 0
|
||||||
|
&& strcmp(g_last_state_detail, safe_detail) == 0
|
||||||
|
&& now_ms - g_last_state_write_ms < DAEMON_STATE_HEARTBEAT_MS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
safe_copy(g_last_state_phase, sizeof(g_last_state_phase), safe_phase);
|
||||||
|
safe_copy(g_last_state_subject, sizeof(g_last_state_subject), safe_subject);
|
||||||
|
safe_copy(g_last_state_detail, sizeof(g_last_state_detail), safe_detail);
|
||||||
|
g_last_state_write_ms = now_ms;
|
||||||
|
|
||||||
char now[32] = {};
|
char now[32] = {};
|
||||||
now_string(now, sizeof(now));
|
now_string(now, sizeof(now));
|
||||||
|
|
||||||
@@ -33,9 +56,9 @@ static void write_daemon_state(const char* phase, const char* subject = nullptr,
|
|||||||
"time=%s\npid=%d\nphase=%s\nsubject=%s\ndetail=%s\n",
|
"time=%s\npid=%d\nphase=%s\nsubject=%s\ndetail=%s\n",
|
||||||
now,
|
now,
|
||||||
montauk::getpid(),
|
montauk::getpid(),
|
||||||
phase ? phase : "",
|
safe_phase,
|
||||||
subject ? subject : "",
|
safe_subject,
|
||||||
detail ? detail : "");
|
safe_detail);
|
||||||
if (n < 0) return;
|
if (n < 0) return;
|
||||||
write_text_file_atomic(SPOOL_DAEMON_STATE_PATH, buf);
|
write_text_file_atomic(SPOOL_DAEMON_STATE_PATH, buf);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -423,11 +423,35 @@ static void compute_config_layout(ConfigLayout* lo) {
|
|||||||
lo->cancel_btn = {pad + 88, g_config.height - BTN_H - 16, 80, BTN_H};
|
lo->cancel_btn = {pad + 88, g_config.height - BTN_H - 16, 80, BTN_H};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void refresh_state(bool force = false) {
|
static bool refresh_state(bool force = false) {
|
||||||
uint64_t now = montauk::get_milliseconds();
|
uint64_t now = montauk::get_milliseconds();
|
||||||
if (!force && g_app.printers_loaded && now - g_app.last_refresh_ms < 1000) return;
|
if (!force && g_app.printers_loaded && now - g_app.last_refresh_ms < 1000) return false;
|
||||||
|
|
||||||
ensure_spool_dirs();
|
bool was_loaded = g_app.printers_loaded;
|
||||||
|
char old_printer_uri[MAX_PATH_LEN];
|
||||||
|
safe_copy(old_printer_uri, sizeof(old_printer_uri), g_app.printer_uri);
|
||||||
|
bool old_daemon_running = g_app.daemon_running;
|
||||||
|
int old_daemon_pid = g_app.daemon_pid;
|
||||||
|
int old_queue_count = g_app.queue_count;
|
||||||
|
int old_active_count = g_app.active_count;
|
||||||
|
int old_done_count = g_app.done_count;
|
||||||
|
int old_failed_count = g_app.failed_count;
|
||||||
|
JobMeta old_active = g_app.last_active;
|
||||||
|
JobMeta old_failed = g_app.last_failed;
|
||||||
|
bool old_has_active = g_app.has_active;
|
||||||
|
bool old_has_failed = g_app.has_failed;
|
||||||
|
IppCapabilities old_probe_caps = g_app.probe_caps;
|
||||||
|
bool old_probe_valid = g_app.probe_valid;
|
||||||
|
bool old_probe_ok = g_app.probe_ok;
|
||||||
|
char old_probe_message[sizeof(g_app.probe_message)];
|
||||||
|
char old_probe_time[sizeof(g_app.probe_time)];
|
||||||
|
char old_probe_detail[sizeof(g_app.probe_detail)];
|
||||||
|
safe_copy(old_probe_message, sizeof(old_probe_message), g_app.probe_message);
|
||||||
|
safe_copy(old_probe_time, sizeof(old_probe_time), g_app.probe_time);
|
||||||
|
safe_copy(old_probe_detail, sizeof(old_probe_detail), g_app.probe_detail);
|
||||||
|
|
||||||
|
if (force || !g_app.printers_loaded)
|
||||||
|
ensure_spool_dirs();
|
||||||
|
|
||||||
if (!read_default_printer_uri(g_app.printer_uri, sizeof(g_app.printer_uri)))
|
if (!read_default_printer_uri(g_app.printer_uri, sizeof(g_app.printer_uri)))
|
||||||
g_app.printer_uri[0] = '\0';
|
g_app.printer_uri[0] = '\0';
|
||||||
@@ -449,6 +473,25 @@ static void refresh_state(bool force = false) {
|
|||||||
g_app.has_failed = load_latest_job(SPOOL_FAILED_DIR, &g_app.last_failed);
|
g_app.has_failed = load_latest_job(SPOOL_FAILED_DIR, &g_app.last_failed);
|
||||||
g_app.printers_loaded = true;
|
g_app.printers_loaded = true;
|
||||||
g_app.last_refresh_ms = now;
|
g_app.last_refresh_ms = now;
|
||||||
|
|
||||||
|
return !was_loaded
|
||||||
|
|| !montauk::streq(old_printer_uri, g_app.printer_uri)
|
||||||
|
|| old_daemon_running != g_app.daemon_running
|
||||||
|
|| old_daemon_pid != g_app.daemon_pid
|
||||||
|
|| old_queue_count != g_app.queue_count
|
||||||
|
|| old_active_count != g_app.active_count
|
||||||
|
|| old_done_count != g_app.done_count
|
||||||
|
|| old_failed_count != g_app.failed_count
|
||||||
|
|| old_has_active != g_app.has_active
|
||||||
|
|| (g_app.has_active && memcmp(&old_active, &g_app.last_active, sizeof(g_app.last_active)) != 0)
|
||||||
|
|| old_has_failed != g_app.has_failed
|
||||||
|
|| (g_app.has_failed && memcmp(&old_failed, &g_app.last_failed, sizeof(g_app.last_failed)) != 0)
|
||||||
|
|| old_probe_valid != g_app.probe_valid
|
||||||
|
|| old_probe_ok != g_app.probe_ok
|
||||||
|
|| !montauk::streq(old_probe_message, g_app.probe_message)
|
||||||
|
|| !montauk::streq(old_probe_time, g_app.probe_time)
|
||||||
|
|| !montauk::streq(old_probe_detail, g_app.probe_detail)
|
||||||
|
|| memcmp(&old_probe_caps, &g_app.probe_caps, sizeof(g_app.probe_caps)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void probe_printer() {
|
static void probe_printer() {
|
||||||
@@ -1207,10 +1250,12 @@ extern "C" void _start() {
|
|||||||
int r = g_win.poll(&ev);
|
int r = g_win.poll(&ev);
|
||||||
if (r < 0) break;
|
if (r < 0) break;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
uint64_t last_refresh = g_app.last_refresh_ms;
|
if (refresh_state(false))
|
||||||
refresh_state(false);
|
|
||||||
if (g_app.last_refresh_ms != last_refresh)
|
|
||||||
redraw_main = true;
|
redraw_main = true;
|
||||||
|
if (g_app.status_msg[0] && !status_visible()) {
|
||||||
|
g_app.status_msg[0] = '\0';
|
||||||
|
redraw_main = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (redraw_main)
|
if (redraw_main)
|
||||||
render();
|
render();
|
||||||
|
|||||||
Reference in New Issue
Block a user