feat: printer UX improvements
This commit is contained in:
@@ -284,6 +284,17 @@ namespace WinServer {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slot.liveSurface != nullptr) {
|
||||||
|
Ipc::CopySurfacePreserve(newLive, newW, newH,
|
||||||
|
slot.liveSurface, slot.width, slot.height,
|
||||||
|
0xFFFFFFFFu);
|
||||||
|
}
|
||||||
|
if (slot.snapshotSurface != nullptr) {
|
||||||
|
Ipc::CopySurfacePreserve(newSnapshot, newW, newH,
|
||||||
|
slot.snapshotSurface, slot.width, slot.height,
|
||||||
|
0xFFFFFFFFu);
|
||||||
|
}
|
||||||
|
|
||||||
if (slot.liveSurface != nullptr) {
|
if (slot.liveSurface != nullptr) {
|
||||||
Ipc::UnmapSurfaceForPid(slot.liveSurface, callerPid, ownerPml4);
|
Ipc::UnmapSurfaceForPid(slot.liveSurface, callerPid, ownerPml4);
|
||||||
Ipc::ReleaseSurface(slot.liveSurface);
|
Ipc::ReleaseSurface(slot.liveSurface);
|
||||||
|
|||||||
@@ -118,6 +118,15 @@ namespace Ipc {
|
|||||||
uint32_t numPages;
|
uint32_t numPages;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline uint32_t* SurfacePixelPtr(Surface* surface, uint64_t pixelIndex) {
|
||||||
|
if (surface == nullptr) return nullptr;
|
||||||
|
uint64_t byteOffset = pixelIndex * sizeof(uint32_t);
|
||||||
|
uint32_t pageIndex = (uint32_t)(byteOffset / 0x1000ULL);
|
||||||
|
uint32_t pageOffset = (uint32_t)(byteOffset % 0x1000ULL);
|
||||||
|
if (pageIndex >= surface->numPages) return nullptr;
|
||||||
|
return (uint32_t*)((uint8_t*)Memory::HHDM(surface->physPages[pageIndex]) + pageOffset);
|
||||||
|
}
|
||||||
|
|
||||||
static HandleEntry g_handleTables[Sched::MaxProcesses][MaxHandlesPerProcess] = {};
|
static HandleEntry g_handleTables[Sched::MaxProcesses][MaxHandlesPerProcess] = {};
|
||||||
static SurfaceMap g_surfaceMaps[Sched::MaxProcesses][MaxSurfaceMapsPerProcess] = {};
|
static SurfaceMap g_surfaceMaps[Sched::MaxProcesses][MaxSurfaceMapsPerProcess] = {};
|
||||||
|
|
||||||
@@ -1381,6 +1390,40 @@ namespace Ipc {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CopySurfacePreserve(Surface* dst, int dstWidth, int dstHeight,
|
||||||
|
Surface* src, int srcWidth, int srcHeight,
|
||||||
|
uint32_t fillPixel) {
|
||||||
|
if (dst == nullptr || src == nullptr) return -1;
|
||||||
|
if (dstWidth <= 0 || dstHeight <= 0 || srcWidth <= 0 || srcHeight <= 0) return -1;
|
||||||
|
|
||||||
|
uint64_t dstPixels = (uint64_t)dstWidth * (uint64_t)dstHeight;
|
||||||
|
uint64_t srcPixels = (uint64_t)srcWidth * (uint64_t)srcHeight;
|
||||||
|
if (dstPixels * sizeof(uint32_t) > dst->sizeBytes) return -1;
|
||||||
|
if (srcPixels * sizeof(uint32_t) > src->sizeBytes) return -1;
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < dstPixels; i++) {
|
||||||
|
uint32_t* p = SurfacePixelPtr(dst, i);
|
||||||
|
if (p != nullptr) *p = fillPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
int copyWidth = (dstWidth < srcWidth) ? dstWidth : srcWidth;
|
||||||
|
int copyHeight = (dstHeight < srcHeight) ? dstHeight : srcHeight;
|
||||||
|
for (int y = 0; y < copyHeight; y++) {
|
||||||
|
uint64_t dstRow = (uint64_t)y * (uint64_t)dstWidth;
|
||||||
|
uint64_t srcRow = (uint64_t)y * (uint64_t)srcWidth;
|
||||||
|
for (int x = 0; x < copyWidth; x++) {
|
||||||
|
uint32_t* srcPixel = SurfacePixelPtr(src, srcRow + (uint64_t)x);
|
||||||
|
uint32_t* dstPixel = SurfacePixelPtr(dst, dstRow + (uint64_t)x);
|
||||||
|
if (srcPixel != nullptr && dstPixel != nullptr) {
|
||||||
|
*dstPixel = *srcPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotifyObjectChanged((Object*)dst);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int MapSurfaceForPid(Surface* surface, int pid, uint64_t pml4Phys, uint64_t& heapNext, uint64_t& outVa) {
|
int MapSurfaceForPid(Surface* surface, int pid, uint64_t pml4Phys, uint64_t& heapNext, uint64_t& outVa) {
|
||||||
if (surface == nullptr) return -1;
|
if (surface == nullptr) return -1;
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ namespace Ipc {
|
|||||||
uint64_t GetSurfaceSize(const Surface* surface);
|
uint64_t GetSurfaceSize(const Surface* surface);
|
||||||
int ResizeSurface(Surface* surface, uint64_t newSize);
|
int ResizeSurface(Surface* surface, uint64_t newSize);
|
||||||
int CopySurface(Surface* dst, Surface* src);
|
int CopySurface(Surface* dst, Surface* src);
|
||||||
|
int CopySurfacePreserve(Surface* dst, int dstWidth, int dstHeight,
|
||||||
|
Surface* src, int srcWidth, int srcHeight,
|
||||||
|
uint32_t fillPixel);
|
||||||
uint64_t MapSurfaceHandle(int handle);
|
uint64_t MapSurfaceHandle(int handle);
|
||||||
int ResizeSurfaceHandle(int handle, uint64_t newSize);
|
int ResizeSurfaceHandle(int handle, uint64_t newSize);
|
||||||
int MapSurfaceForPid(Surface* surface, int pid, uint64_t pml4Phys, uint64_t& heapNext, uint64_t& outVa);
|
int MapSurfaceForPid(Surface* surface, int pid, uint64_t pml4Phys, uint64_t& heapNext, uint64_t& outVa);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ static constexpr const char* SPOOL_FAILED_DIR = "0:/spool/print/failed";
|
|||||||
static constexpr const char* SPOOL_DOCS_DIR = "0:/spool/print/docs";
|
static constexpr const char* SPOOL_DOCS_DIR = "0:/spool/print/docs";
|
||||||
static constexpr const char* SPOOL_TMP_DIR = "0:/spool/print/tmp";
|
static constexpr const char* SPOOL_TMP_DIR = "0:/spool/print/tmp";
|
||||||
static constexpr const char* SPOOL_DAEMON_PID_PATH = "0:/spool/print/daemon.pid";
|
static constexpr const char* SPOOL_DAEMON_PID_PATH = "0:/spool/print/daemon.pid";
|
||||||
|
static constexpr const char* SPOOL_PROBE_STATE_PATH = "0:/spool/print/printer-probe.state";
|
||||||
static constexpr const char* PRINTD_BINARY = "0:/os/printd.elf";
|
static constexpr const char* PRINTD_BINARY = "0:/os/printd.elf";
|
||||||
static constexpr int MAX_PATH_LEN = 256;
|
static constexpr int MAX_PATH_LEN = 256;
|
||||||
static constexpr int MAX_JOB_NAME_LEN = 128;
|
static constexpr int MAX_JOB_NAME_LEN = 128;
|
||||||
@@ -88,6 +89,15 @@ struct IppCapabilities {
|
|||||||
char preferred_format[64];
|
char preferred_format[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ProbeState {
|
||||||
|
bool ok;
|
||||||
|
char printer_uri[MAX_PATH_LEN];
|
||||||
|
char probed_at[32];
|
||||||
|
char message[128];
|
||||||
|
char detail[MAX_DEBUG_LEN];
|
||||||
|
IppCapabilities caps;
|
||||||
|
};
|
||||||
|
|
||||||
struct IppPrintResult {
|
struct IppPrintResult {
|
||||||
bool ok;
|
bool ok;
|
||||||
bool use_tls;
|
bool use_tls;
|
||||||
@@ -120,6 +130,10 @@ inline void zero_job(JobMeta* job) {
|
|||||||
if (job) memset(job, 0, sizeof(*job));
|
if (job) memset(job, 0, sizeof(*job));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void zero_probe_state(ProbeState* state) {
|
||||||
|
if (state) memset(state, 0, sizeof(*state));
|
||||||
|
}
|
||||||
|
|
||||||
inline void safe_copy(char* dst, int dst_len, const char* src) {
|
inline void safe_copy(char* dst, int dst_len, const char* src) {
|
||||||
if (dst == nullptr || dst_len <= 0) return;
|
if (dst == nullptr || dst_len <= 0) return;
|
||||||
if (src == nullptr) {
|
if (src == nullptr) {
|
||||||
@@ -697,6 +711,112 @@ inline bool load_job_from_path(const char* path, JobMeta* job) {
|
|||||||
return job->id[0] != '\0';
|
return job->id[0] != '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool save_probe_state_atomic(const char* path, const ProbeState* state) {
|
||||||
|
if (path == nullptr || state == nullptr) return false;
|
||||||
|
char tmp[MAX_PATH_LEN];
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||||
|
|
||||||
|
FILE* f = fopen(tmp, "wb");
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
|
ProbeState copy = *state;
|
||||||
|
sanitize_field(copy.printer_uri);
|
||||||
|
sanitize_field(copy.probed_at);
|
||||||
|
sanitize_field(copy.message);
|
||||||
|
sanitize_field(copy.detail);
|
||||||
|
sanitize_field(copy.caps.host);
|
||||||
|
sanitize_field(copy.caps.path);
|
||||||
|
sanitize_field(copy.caps.printer_name);
|
||||||
|
sanitize_field(copy.caps.status_message);
|
||||||
|
sanitize_field(copy.caps.supported_formats);
|
||||||
|
sanitize_field(copy.caps.default_format);
|
||||||
|
sanitize_field(copy.caps.preferred_format);
|
||||||
|
|
||||||
|
fprintf(f, "ok=%d\n", copy.ok ? 1 : 0);
|
||||||
|
fprintf(f, "printer_uri=%s\n", copy.printer_uri);
|
||||||
|
fprintf(f, "probed_at=%s\n", copy.probed_at);
|
||||||
|
fprintf(f, "message=%s\n", copy.message);
|
||||||
|
fprintf(f, "detail=%s\n", copy.detail);
|
||||||
|
fprintf(f, "caps_ok=%d\n", copy.caps.ok ? 1 : 0);
|
||||||
|
fprintf(f, "caps_supports_pdf=%d\n", copy.caps.supports_pdf ? 1 : 0);
|
||||||
|
fprintf(f, "caps_supports_text=%d\n", copy.caps.supports_text ? 1 : 0);
|
||||||
|
fprintf(f, "caps_supports_jpeg=%d\n", copy.caps.supports_jpeg ? 1 : 0);
|
||||||
|
fprintf(f, "caps_use_tls=%d\n", copy.caps.use_tls ? 1 : 0);
|
||||||
|
fprintf(f, "caps_http_status=%d\n", copy.caps.http_status);
|
||||||
|
fprintf(f, "caps_ipp_status=%u\n", (unsigned)copy.caps.ipp_status);
|
||||||
|
fprintf(f, "caps_request_id=%d\n", copy.caps.request_id);
|
||||||
|
fprintf(f, "caps_resolved_ip=%u\n", (unsigned)copy.caps.resolved_ip);
|
||||||
|
fprintf(f, "caps_port=%u\n", (unsigned)copy.caps.port);
|
||||||
|
fprintf(f, "caps_host=%s\n", copy.caps.host);
|
||||||
|
fprintf(f, "caps_path=%s\n", copy.caps.path);
|
||||||
|
fprintf(f, "caps_printer_name=%s\n", copy.caps.printer_name);
|
||||||
|
fprintf(f, "caps_status_message=%s\n", copy.caps.status_message);
|
||||||
|
fprintf(f, "caps_supported_formats=%s\n", copy.caps.supported_formats);
|
||||||
|
fprintf(f, "caps_default_format=%s\n", copy.caps.default_format);
|
||||||
|
fprintf(f, "caps_preferred_format=%s\n", copy.caps.preferred_format);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (rename(tmp, path) != 0) {
|
||||||
|
remove(tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool load_probe_state(const char* path, ProbeState* state) {
|
||||||
|
if (path == nullptr || state == nullptr) return false;
|
||||||
|
zero_probe_state(state);
|
||||||
|
|
||||||
|
FILE* f = fopen(path, "rb");
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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, "message") == 0) safe_copy(state->message, sizeof(state->message), eq);
|
||||||
|
else if (strcmp(line, "detail") == 0) safe_copy(state->detail, sizeof(state->detail), eq);
|
||||||
|
else if (strcmp(line, "caps_ok") == 0) state->caps.ok = atoi(eq) != 0;
|
||||||
|
else if (strcmp(line, "caps_supports_pdf") == 0) state->caps.supports_pdf = atoi(eq) != 0;
|
||||||
|
else if (strcmp(line, "caps_supports_text") == 0) state->caps.supports_text = atoi(eq) != 0;
|
||||||
|
else if (strcmp(line, "caps_supports_jpeg") == 0) state->caps.supports_jpeg = atoi(eq) != 0;
|
||||||
|
else if (strcmp(line, "caps_use_tls") == 0) state->caps.use_tls = atoi(eq) != 0;
|
||||||
|
else if (strcmp(line, "caps_http_status") == 0) state->caps.http_status = atoi(eq);
|
||||||
|
else if (strcmp(line, "caps_ipp_status") == 0) state->caps.ipp_status = (uint16_t)atoi(eq);
|
||||||
|
else if (strcmp(line, "caps_request_id") == 0) state->caps.request_id = atoi(eq);
|
||||||
|
else if (strcmp(line, "caps_resolved_ip") == 0) state->caps.resolved_ip = (uint32_t)strtoul(eq, nullptr, 10);
|
||||||
|
else if (strcmp(line, "caps_port") == 0) state->caps.port = (uint16_t)atoi(eq);
|
||||||
|
else if (strcmp(line, "caps_host") == 0) safe_copy(state->caps.host, sizeof(state->caps.host), eq);
|
||||||
|
else if (strcmp(line, "caps_path") == 0) safe_copy(state->caps.path, sizeof(state->caps.path), eq);
|
||||||
|
else if (strcmp(line, "caps_printer_name") == 0) safe_copy(state->caps.printer_name, sizeof(state->caps.printer_name), eq);
|
||||||
|
else if (strcmp(line, "caps_status_message") == 0) safe_copy(state->caps.status_message, sizeof(state->caps.status_message), 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_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';
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool save_printer_probe_state(const ProbeState* state) {
|
||||||
|
ensure_spool_dirs();
|
||||||
|
return save_probe_state_atomic(SPOOL_PROBE_STATE_PATH, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool load_printer_probe_state(ProbeState* state) {
|
||||||
|
return load_probe_state(SPOOL_PROBE_STATE_PATH, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear_printer_probe_state() {
|
||||||
|
remove(SPOOL_PROBE_STATE_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool find_job(const char* id, char* out_path, int out_path_len, char* out_state, int out_state_len) {
|
inline bool find_job(const char* id, char* out_path, int out_path_len, char* out_state, int out_state_len) {
|
||||||
if (id == nullptr || *id == '\0') return false;
|
if (id == nullptr || *id == '\0') return false;
|
||||||
const struct {
|
const struct {
|
||||||
@@ -1321,6 +1441,18 @@ inline void summarize_ipp_capabilities(const IppCapabilities* caps, char* out, i
|
|||||||
caps->supports_text ? "yes" : "no");
|
caps->supports_text ? "yes" : "no");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void summarize_probe_transport(const IppUri* uri, const char* resolution, char* out, int out_len) {
|
||||||
|
if (out == nullptr || out_len <= 0) return;
|
||||||
|
out[0] = '\0';
|
||||||
|
if (uri == nullptr) return;
|
||||||
|
snprintf(out, (size_t)out_len, "host=%s ip=%s port=%u path=%s %s",
|
||||||
|
uri->host,
|
||||||
|
(resolution && *resolution) ? resolution : "unresolved",
|
||||||
|
(unsigned)uri->port,
|
||||||
|
uri->path[0] ? uri->path : "/",
|
||||||
|
uri->use_tls ? "tls" : "plain");
|
||||||
|
}
|
||||||
|
|
||||||
inline void summarize_ipp_print_result(const IppPrintResult* result, char* out, int out_len) {
|
inline void summarize_ipp_print_result(const IppPrintResult* result, char* out, int out_len) {
|
||||||
if (out == nullptr || out_len <= 0) return;
|
if (out == nullptr || out_len <= 0) return;
|
||||||
out[0] = '\0';
|
out[0] = '\0';
|
||||||
@@ -1516,6 +1648,64 @@ inline bool ipp_get_printer_capabilities(const char* printer_uri,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool probe_printer_uri(const char* printer_uri, ProbeState* out, char* err, int err_len) {
|
||||||
|
if (out == nullptr) return false;
|
||||||
|
zero_probe_state(out);
|
||||||
|
now_string(out->probed_at, sizeof(out->probed_at));
|
||||||
|
if (printer_uri && *printer_uri) safe_copy(out->printer_uri, sizeof(out->printer_uri), printer_uri);
|
||||||
|
|
||||||
|
if (printer_uri == nullptr || *printer_uri == '\0') {
|
||||||
|
safe_copy(out->message, sizeof(out->message), "No printer configured");
|
||||||
|
safe_copy(err, err_len, out->message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IppUri normalized = {};
|
||||||
|
char local_err[128] = {};
|
||||||
|
if (!normalize_ipp_uri(printer_uri, &normalized, local_err, sizeof(local_err))) {
|
||||||
|
safe_copy(out->message, sizeof(out->message), local_err);
|
||||||
|
safe_copy(err, err_len, local_err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_copy(out->printer_uri, sizeof(out->printer_uri), normalized.normalized);
|
||||||
|
|
||||||
|
uint32_t ip = 0;
|
||||||
|
if (!resolve_host(normalized.host, &ip)) {
|
||||||
|
summarize_probe_transport(&normalized, "unresolved", out->detail, sizeof(out->detail));
|
||||||
|
if (host_looks_like_mdns(normalized.host))
|
||||||
|
safe_copy(out->message, sizeof(out->message), ".local/mDNS hostnames are not supported yet");
|
||||||
|
else
|
||||||
|
snprintf(out->message, sizeof(out->message), "Could not resolve %s", normalized.host);
|
||||||
|
safe_copy(err, err_len, out->message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ip_text[20];
|
||||||
|
format_ipv4(ip_text, sizeof(ip_text), ip);
|
||||||
|
summarize_probe_transport(&normalized, ip_text, out->detail, sizeof(out->detail));
|
||||||
|
|
||||||
|
if (!ipp_get_printer_capabilities(normalized.normalized, &out->caps, local_err, sizeof(local_err))) {
|
||||||
|
if (out->caps.host[0] == '\0') safe_copy(out->caps.host, sizeof(out->caps.host), normalized.host);
|
||||||
|
if (out->caps.path[0] == '\0') safe_copy(out->caps.path, sizeof(out->caps.path), normalized.path);
|
||||||
|
if (out->caps.port == 0) out->caps.port = normalized.port;
|
||||||
|
out->caps.resolved_ip = ip;
|
||||||
|
out->caps.use_tls = normalized.use_tls;
|
||||||
|
char summary[MAX_DEBUG_LEN] = {};
|
||||||
|
summarize_ipp_capabilities(&out->caps, summary, sizeof(summary));
|
||||||
|
if (summary[0]) safe_copy(out->detail, sizeof(out->detail), summary);
|
||||||
|
safe_copy(out->message, sizeof(out->message), local_err[0] ? local_err : "IPP probe failed");
|
||||||
|
safe_copy(err, err_len, out->message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
summarize_ipp_capabilities(&out->caps, out->detail, sizeof(out->detail));
|
||||||
|
safe_copy(out->message, sizeof(out->message), "Printer probe succeeded");
|
||||||
|
out->ok = true;
|
||||||
|
if (err && err_len > 0) err[0] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool ipp_print_buffer(const char* printer_uri,
|
inline bool ipp_print_buffer(const char* printer_uri,
|
||||||
const char* job_name,
|
const char* job_name,
|
||||||
const char* user_name,
|
const char* user_name,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ CXXFLAGS := \
|
|||||||
-mcmodel=small \
|
-mcmodel=small \
|
||||||
-I $(PROG_INC) \
|
-I $(PROG_INC) \
|
||||||
-isystem $(PROG_INC)/libc \
|
-isystem $(PROG_INC)/libc \
|
||||||
|
-I ../../lib/bearssl/inc \
|
||||||
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
-isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \
|
||||||
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
-isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ LDFLAGS := \
|
|||||||
|
|
||||||
# ---- Source files ----
|
# ---- Source files ----
|
||||||
|
|
||||||
SRCS := main.cpp render.cpp diskdetail.cpp stb_truetype_impl.cpp
|
SRCS := main.cpp render.cpp diskdetail.cpp printers.cpp stb_truetype_impl.cpp
|
||||||
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
|
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
|
||||||
|
|
||||||
# ---- Target ----
|
# ---- Target ----
|
||||||
|
|||||||
@@ -29,11 +29,27 @@ static constexpr int TOOLBAR_H = 36;
|
|||||||
static constexpr int CAT_H = 28;
|
static constexpr int CAT_H = 28;
|
||||||
static constexpr int ITEM_H = 24;
|
static constexpr int ITEM_H = 24;
|
||||||
static constexpr int MAX_DEVS = 64;
|
static constexpr int MAX_DEVS = 64;
|
||||||
|
static constexpr int MAX_VIRTUAL_DEVS = 4;
|
||||||
|
static constexpr int MAX_TOTAL_DEVS = MAX_DEVS + MAX_VIRTUAL_DEVS;
|
||||||
static constexpr int POLL_MS = 2000;
|
static constexpr int POLL_MS = 2000;
|
||||||
static constexpr int INDENT = 28;
|
static constexpr int INDENT = 28;
|
||||||
static constexpr int FONT_SIZE = 18;
|
static constexpr int FONT_SIZE = 18;
|
||||||
|
|
||||||
static constexpr int NUM_CATEGORIES = 9;
|
enum DeviceCategory {
|
||||||
|
CAT_CPU = 0,
|
||||||
|
CAT_INTERRUPT,
|
||||||
|
CAT_TIMER,
|
||||||
|
CAT_INPUT,
|
||||||
|
CAT_USB,
|
||||||
|
CAT_NETWORK,
|
||||||
|
CAT_DISPLAY,
|
||||||
|
CAT_STORAGE,
|
||||||
|
CAT_PCI,
|
||||||
|
CAT_AUDIO,
|
||||||
|
CAT_ACPI,
|
||||||
|
CAT_PRINTER,
|
||||||
|
NUM_CATEGORIES
|
||||||
|
};
|
||||||
|
|
||||||
static constexpr Color BG_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
static constexpr Color BG_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
||||||
static constexpr Color TOOLBAR_BG = Color::from_rgb(0xF5, 0xF5, 0xF5);
|
static constexpr Color TOOLBAR_BG = Color::from_rgb(0xF5, 0xF5, 0xF5);
|
||||||
@@ -48,27 +64,33 @@ static constexpr Color ACCENT_COLOR = Color::from_rgb(0x33, 0x66, 0xCC);
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
static const char* category_names[] = {
|
static const char* category_names[] = {
|
||||||
"CPU", // 0
|
"CPU",
|
||||||
"Interrupt", // 1
|
"Interrupt",
|
||||||
"Timer", // 2
|
"Timer",
|
||||||
"Input", // 3
|
"Input",
|
||||||
"USB", // 4
|
"USB",
|
||||||
"Network", // 5
|
"Network",
|
||||||
"Display", // 6
|
"Display",
|
||||||
"Storage", // 7
|
"Storage",
|
||||||
"PCI", // 8
|
"PCI",
|
||||||
|
"Audio",
|
||||||
|
"ACPI",
|
||||||
|
"Printers",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const Color category_colors[] = {
|
static const Color category_colors[] = {
|
||||||
Color::from_rgb(0x33, 0x66, 0xCC), // CPU - blue
|
Color::from_rgb(0x33, 0x66, 0xCC),
|
||||||
Color::from_rgb(0x88, 0x44, 0xAA), // Interrupt - purple
|
Color::from_rgb(0x88, 0x44, 0xAA),
|
||||||
Color::from_rgb(0x22, 0x88, 0x22), // Timer - green
|
Color::from_rgb(0x22, 0x88, 0x22),
|
||||||
Color::from_rgb(0xCC, 0x88, 0x00), // Input - amber
|
Color::from_rgb(0xCC, 0x88, 0x00),
|
||||||
Color::from_rgb(0x00, 0x88, 0x88), // USB - teal
|
Color::from_rgb(0x00, 0x88, 0x88),
|
||||||
Color::from_rgb(0xCC, 0x55, 0x22), // Network - orange
|
Color::from_rgb(0xCC, 0x55, 0x22),
|
||||||
Color::from_rgb(0x44, 0x66, 0xCC), // Display - indigo
|
Color::from_rgb(0x44, 0x66, 0xCC),
|
||||||
Color::from_rgb(0x99, 0x55, 0x00), // Storage - brown
|
Color::from_rgb(0x99, 0x55, 0x00),
|
||||||
Color::from_rgb(0x66, 0x66, 0x66), // PCI - gray
|
Color::from_rgb(0x66, 0x66, 0x66),
|
||||||
|
Color::from_rgb(0x8A, 0x5A, 0xCC),
|
||||||
|
Color::from_rgb(0x55, 0x77, 0x88),
|
||||||
|
Color::from_rgb(0x2F, 0x8B, 0x57),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -83,7 +105,7 @@ struct DisplayRow {
|
|||||||
int dev_index;
|
int dev_index;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr int MAX_DISPLAY_ROWS = MAX_DEVS + NUM_CATEGORIES;
|
static constexpr int MAX_DISPLAY_ROWS = MAX_TOTAL_DEVS + NUM_CATEGORIES;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Disk detail window
|
// Disk detail window
|
||||||
@@ -110,7 +132,7 @@ struct DiskDetailState {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
struct DevExplorerState {
|
struct DevExplorerState {
|
||||||
Montauk::DevInfo devs[MAX_DEVS];
|
Montauk::DevInfo devs[MAX_TOTAL_DEVS];
|
||||||
int dev_count;
|
int dev_count;
|
||||||
bool collapsed[NUM_CATEGORIES];
|
bool collapsed[NUM_CATEGORIES];
|
||||||
int selected_row;
|
int selected_row;
|
||||||
@@ -142,6 +164,7 @@ void render(uint32_t* pixels);
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
int build_display_rows(DevExplorerState* de, DisplayRow* rows);
|
int build_display_rows(DevExplorerState* de, DisplayRow* rows);
|
||||||
|
int append_printer_devices(Montauk::DevInfo* out, int max_count);
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Function declarations — diskdetail.cpp
|
// Function declarations — diskdetail.cpp
|
||||||
|
|||||||
@@ -17,6 +17,21 @@ int g_win_h = INIT_H;
|
|||||||
DevExplorerState g_state;
|
DevExplorerState g_state;
|
||||||
TrueTypeFont* g_font = nullptr;
|
TrueTypeFont* g_font = nullptr;
|
||||||
|
|
||||||
|
static void refresh_devices(DevExplorerState* de) {
|
||||||
|
if (de == nullptr) return;
|
||||||
|
|
||||||
|
int kernel_count = montauk::devlist(de->devs, MAX_DEVS);
|
||||||
|
if (kernel_count < 0) kernel_count = 0;
|
||||||
|
if (kernel_count > MAX_DEVS) kernel_count = MAX_DEVS;
|
||||||
|
|
||||||
|
int printer_count = append_printer_devices(
|
||||||
|
de->devs + kernel_count, MAX_TOTAL_DEVS - kernel_count);
|
||||||
|
if (printer_count < 0) printer_count = 0;
|
||||||
|
|
||||||
|
de->dev_count = kernel_count + printer_count;
|
||||||
|
de->last_poll_ms = montauk::get_milliseconds();
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Display row building
|
// Display row building
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -92,7 +107,7 @@ static bool handle_list_click(int mx, int my) {
|
|||||||
bool is_double = (de.last_click_row == i)
|
bool is_double = (de.last_click_row == i)
|
||||||
&& (now - de.last_click_ms < 400);
|
&& (now - de.last_click_ms < 400);
|
||||||
|
|
||||||
if (is_double && de.devs[di].category == 7) {
|
if (is_double && de.devs[di].category == CAT_STORAGE) {
|
||||||
int port = (int)de.devs[di]._pad[0];
|
int port = (int)de.devs[di]._pad[0];
|
||||||
open_disk_detail(port, de.devs[di].name);
|
open_disk_detail(port, de.devs[di].name);
|
||||||
de.last_click_row = -1;
|
de.last_click_row = -1;
|
||||||
@@ -201,8 +216,7 @@ extern "C" void _start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initial device poll
|
// Initial device poll
|
||||||
g_state.dev_count = montauk::devlist(g_state.devs, MAX_DEVS);
|
refresh_devices(&g_state);
|
||||||
g_state.last_poll_ms = montauk::get_milliseconds();
|
|
||||||
|
|
||||||
WsWindow win;
|
WsWindow win;
|
||||||
if (!win.create("Devices", INIT_W, INIT_H))
|
if (!win.create("Devices", INIT_W, INIT_H))
|
||||||
@@ -221,8 +235,7 @@ extern "C" void _start() {
|
|||||||
// Poll for device refresh
|
// Poll for device refresh
|
||||||
uint64_t now = montauk::get_milliseconds();
|
uint64_t now = montauk::get_milliseconds();
|
||||||
if (now - g_state.last_poll_ms >= POLL_MS) {
|
if (now - g_state.last_poll_ms >= POLL_MS) {
|
||||||
g_state.dev_count = montauk::devlist(g_state.devs, MAX_DEVS);
|
refresh_devices(&g_state);
|
||||||
g_state.last_poll_ms = now;
|
|
||||||
redraw_main = true;
|
redraw_main = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* printers.cpp
|
||||||
|
* Virtual printer-device registration for the MontaukOS Device Explorer
|
||||||
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "devexplorer.h"
|
||||||
|
#include <print/print.hpp>
|
||||||
|
|
||||||
|
using namespace print;
|
||||||
|
|
||||||
|
static void format_printer_detail(const ProbeState& probe,
|
||||||
|
const IppUri* normalized,
|
||||||
|
char* out, int out_len) {
|
||||||
|
if (out == nullptr || out_len <= 0) return;
|
||||||
|
out[0] = '\0';
|
||||||
|
|
||||||
|
const char* host = nullptr;
|
||||||
|
if (probe.caps.host[0]) host = probe.caps.host;
|
||||||
|
else if (normalized && normalized->host[0]) host = normalized->host;
|
||||||
|
|
||||||
|
if (probe.ok) {
|
||||||
|
if (host && *host)
|
||||||
|
snprintf(out, (size_t)out_len, "Ready on %s", host);
|
||||||
|
else
|
||||||
|
snprintf(out, (size_t)out_len, "Ready");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe.message[0]) {
|
||||||
|
if (strstr(probe.message, "resolve"))
|
||||||
|
snprintf(out, (size_t)out_len, "Host resolution failed");
|
||||||
|
else if (strstr(probe.message, "mDNS"))
|
||||||
|
snprintf(out, (size_t)out_len, "mDNS host not supported");
|
||||||
|
else if (strstr(probe.message, "Unsupported format"))
|
||||||
|
snprintf(out, (size_t)out_len, "Unsupported format");
|
||||||
|
else
|
||||||
|
safe_copy(out, out_len, probe.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host && *host)
|
||||||
|
snprintf(out, (size_t)out_len, "Awaiting probe on %s", host);
|
||||||
|
else
|
||||||
|
snprintf(out, (size_t)out_len, "Awaiting spooler probe");
|
||||||
|
}
|
||||||
|
|
||||||
|
int append_printer_devices(Montauk::DevInfo* out, int max_count) {
|
||||||
|
if (out == nullptr || max_count <= 0) return 0;
|
||||||
|
|
||||||
|
char current_uri[MAX_PATH_LEN] = {};
|
||||||
|
bool has_current_uri = read_default_printer_uri(current_uri, sizeof(current_uri));
|
||||||
|
|
||||||
|
ProbeState probe = {};
|
||||||
|
bool has_probe = load_printer_probe_state(&probe);
|
||||||
|
if (has_probe && has_current_uri && !montauk::streq(probe.printer_uri, current_uri))
|
||||||
|
has_probe = false;
|
||||||
|
|
||||||
|
const char* effective_uri = nullptr;
|
||||||
|
if (has_current_uri) effective_uri = current_uri;
|
||||||
|
else if (has_probe && probe.printer_uri[0]) effective_uri = probe.printer_uri;
|
||||||
|
else return 0;
|
||||||
|
|
||||||
|
IppUri normalized = {};
|
||||||
|
char uri_err[128] = {};
|
||||||
|
bool normalized_ok = normalize_ipp_uri(effective_uri, &normalized, uri_err, sizeof(uri_err));
|
||||||
|
|
||||||
|
Montauk::DevInfo dev = {};
|
||||||
|
dev.category = CAT_PRINTER;
|
||||||
|
|
||||||
|
if (has_probe && probe.caps.printer_name[0]) {
|
||||||
|
safe_copy(dev.name, sizeof(dev.name), probe.caps.printer_name);
|
||||||
|
} else if (normalized_ok && normalized.host[0]) {
|
||||||
|
safe_copy(dev.name, sizeof(dev.name), normalized.host);
|
||||||
|
} else {
|
||||||
|
safe_copy(dev.name, sizeof(dev.name), "Configured Printer");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_probe) {
|
||||||
|
format_printer_detail(probe, normalized_ok ? &normalized : nullptr,
|
||||||
|
dev.detail, sizeof(dev.detail));
|
||||||
|
} else if (normalized_ok && normalized.host[0]) {
|
||||||
|
snprintf(dev.detail, sizeof(dev.detail), "Awaiting probe on %s", normalized.host);
|
||||||
|
} else {
|
||||||
|
safe_copy(dev.detail, sizeof(dev.detail), "Awaiting spooler probe");
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = dev;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -37,6 +37,31 @@ static void print_host_details(const IppUri& uri) {
|
|||||||
printf("Path: %s\n", uri.path);
|
printf("Path: %s\n", uri.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void print_probe_state_summary(const ProbeState& probe, bool verbose) {
|
||||||
|
if (!probe.printer_uri[0] && !probe.message[0]) return;
|
||||||
|
|
||||||
|
printf("Last probe: %s", probe.ok ? "ok" : "failed");
|
||||||
|
if (probe.probed_at[0]) printf(" at %s", probe.probed_at);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
if (!verbose) return;
|
||||||
|
|
||||||
|
if (probe.ok && probe.caps.printer_name[0]) printf("Printer name: %s\n", probe.caps.printer_name);
|
||||||
|
else if (probe.message[0]) printf("Probe status: %s\n", probe.message);
|
||||||
|
|
||||||
|
if (probe.ok) {
|
||||||
|
printf("Supports PDF: %s\n", probe.caps.supports_pdf ? "yes" : "no");
|
||||||
|
printf("Supports JPEG: %s\n", probe.caps.supports_jpeg ? "yes" : "no");
|
||||||
|
printf("Supports text: %s\n", probe.caps.supports_text ? "yes" : "no");
|
||||||
|
if (probe.caps.preferred_format[0]) printf("Preferred format: %s\n", probe.caps.preferred_format);
|
||||||
|
if (probe.caps.default_format[0]) printf("Default format: %s\n", probe.caps.default_format);
|
||||||
|
if (probe.caps.supported_formats[0]) printf("Supported formats: %s\n", probe.caps.supported_formats);
|
||||||
|
if (probe.caps.status_message[0]) printf("Printer status: %s\n", probe.caps.status_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe.detail[0]) printf("Probe detail: %s\n", probe.detail);
|
||||||
|
}
|
||||||
|
|
||||||
static void print_job_detail(const JobMeta& job, const char* state, bool verbose) {
|
static void print_job_detail(const JobMeta& job, const char* state, bool verbose) {
|
||||||
printf(" %s %s\n", job.id, job.job_name);
|
printf(" %s %s\n", job.id, job.job_name);
|
||||||
printf(" state: %s", state ? state : job.state);
|
printf(" state: %s", state ? state : job.state);
|
||||||
@@ -94,7 +119,7 @@ static int cmd_status(const ArgList& args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char uri[MAX_PATH_LEN];
|
char uri[MAX_PATH_LEN] = {};
|
||||||
if (read_default_printer_uri(uri, sizeof(uri))) {
|
if (read_default_printer_uri(uri, sizeof(uri))) {
|
||||||
printf("Default printer: %s\n", uri);
|
printf("Default printer: %s\n", uri);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
@@ -110,6 +135,12 @@ static int cmd_status(const ArgList& args) {
|
|||||||
} else
|
} else
|
||||||
printf("Default printer: not configured\n");
|
printf("Default printer: not configured\n");
|
||||||
|
|
||||||
|
if (uri[0]) {
|
||||||
|
ProbeState probe = {};
|
||||||
|
if (load_printer_probe_state(&probe) && strcmp(probe.printer_uri, uri) == 0)
|
||||||
|
print_probe_state_summary(probe, verbose);
|
||||||
|
}
|
||||||
|
|
||||||
int pid = -1;
|
int pid = -1;
|
||||||
if (daemon_is_running(&pid))
|
if (daemon_is_running(&pid))
|
||||||
printf("Daemon: running (pid %d)\n", pid);
|
printf("Daemon: running (pid %d)\n", pid);
|
||||||
@@ -278,44 +309,39 @@ static int cmd_probe(const char* printer_uri) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t ip = 0;
|
||||||
|
if (resolve_host(normalized.host, &ip)) normalized.ip = ip;
|
||||||
|
|
||||||
printf("Printer URI: %s\n", normalized.normalized);
|
printf("Printer URI: %s\n", normalized.normalized);
|
||||||
print_host_details(normalized);
|
print_host_details(normalized);
|
||||||
|
if (!normalized.ip) {
|
||||||
uint32_t ip = 0;
|
|
||||||
if (!resolve_host(normalized.host, &ip)) {
|
|
||||||
if (host_looks_like_mdns(normalized.host))
|
if (host_looks_like_mdns(normalized.host))
|
||||||
printf("Resolution: unresolved (.local/mDNS not supported yet)\n");
|
printf("Resolution: unresolved (.local/mDNS not supported yet)\n");
|
||||||
else
|
else
|
||||||
printf("Resolution: unresolved\n");
|
printf("Resolution: unresolved\n");
|
||||||
return 1;
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
normalized.ip = ip;
|
|
||||||
char ip_text[20];
|
char ip_text[20];
|
||||||
format_ipv4(ip_text, sizeof(ip_text), ip);
|
format_ipv4(ip_text, sizeof(ip_text), normalized.ip);
|
||||||
printf("Resolution: ok (%s)\n", ip_text);
|
printf("Resolution: ok (%s)\n", ip_text);
|
||||||
|
}
|
||||||
|
|
||||||
IppCapabilities caps = {};
|
ProbeState probe = {};
|
||||||
if (!ipp_get_printer_capabilities(normalized.normalized, &caps, err, sizeof(err))) {
|
if (!probe_printer_uri(normalized.normalized, &probe, err, sizeof(err))) {
|
||||||
char detail[256] = {};
|
printf("Probe failed: %s\n", probe.message[0] ? probe.message : (err[0] ? err : "IPP probe failed"));
|
||||||
summarize_ipp_capabilities(&caps, detail, sizeof(detail));
|
if (probe.detail[0]) printf("Detail: %s\n", probe.detail);
|
||||||
printf("Probe failed: %s\n", err[0] ? err : "IPP probe failed");
|
|
||||||
if (detail[0]) printf("Detail: %s\n", detail);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char detail[256] = {};
|
|
||||||
summarize_ipp_capabilities(&caps, detail, sizeof(detail));
|
|
||||||
printf("Probe: ok\n");
|
printf("Probe: ok\n");
|
||||||
if (caps.printer_name[0]) printf("Printer name: %s\n", caps.printer_name);
|
if (probe.caps.printer_name[0]) printf("Printer name: %s\n", probe.caps.printer_name);
|
||||||
printf("Supports PDF: %s\n", caps.supports_pdf ? "yes" : "no");
|
printf("Supports PDF: %s\n", probe.caps.supports_pdf ? "yes" : "no");
|
||||||
printf("Supports JPEG: %s\n", caps.supports_jpeg ? "yes" : "no");
|
printf("Supports JPEG: %s\n", probe.caps.supports_jpeg ? "yes" : "no");
|
||||||
printf("Supports text: %s\n", caps.supports_text ? "yes" : "no");
|
printf("Supports text: %s\n", probe.caps.supports_text ? "yes" : "no");
|
||||||
if (caps.preferred_format[0]) printf("Preferred format: %s\n", caps.preferred_format);
|
if (probe.caps.preferred_format[0]) printf("Preferred format: %s\n", probe.caps.preferred_format);
|
||||||
if (caps.default_format[0]) printf("Default format: %s\n", caps.default_format);
|
if (probe.caps.default_format[0]) printf("Default format: %s\n", probe.caps.default_format);
|
||||||
if (caps.supported_formats[0]) printf("Supported formats: %s\n", caps.supported_formats);
|
if (probe.caps.supported_formats[0]) printf("Supported formats: %s\n", probe.caps.supported_formats);
|
||||||
if (caps.status_message[0]) printf("Printer status: %s\n", caps.status_message);
|
if (probe.caps.status_message[0]) printf("Printer status: %s\n", probe.caps.status_message);
|
||||||
if (detail[0]) printf("Detail: %s\n", detail);
|
if (probe.detail[0]) printf("Detail: %s\n", probe.detail);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ extern "C" {
|
|||||||
|
|
||||||
using namespace print;
|
using namespace print;
|
||||||
|
|
||||||
|
static constexpr uint64_t PROBE_REFRESH_OK_MS = 60000;
|
||||||
|
static constexpr uint64_t PROBE_REFRESH_FAIL_MS = 15000;
|
||||||
|
|
||||||
static void log_msg(const char* fmt, ...) {
|
static void log_msg(const char* fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
@@ -65,6 +68,56 @@ static bool write_pid_file() {
|
|||||||
return write_text_file_atomic(SPOOL_DAEMON_PID_PATH, pid);
|
return write_text_file_atomic(SPOOL_DAEMON_PID_PATH, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void auto_probe_configured_printer(char* last_uri, int last_uri_len,
|
||||||
|
uint64_t* last_probe_ms, bool* last_probe_ok,
|
||||||
|
bool force = false) {
|
||||||
|
if (last_uri == nullptr || last_probe_ms == nullptr || last_probe_ok == nullptr) return;
|
||||||
|
|
||||||
|
char current_uri[MAX_PATH_LEN] = {};
|
||||||
|
bool have_uri = read_default_printer_uri(current_uri, sizeof(current_uri));
|
||||||
|
uint64_t now = montauk::get_milliseconds();
|
||||||
|
|
||||||
|
if (!have_uri || current_uri[0] == '\0') {
|
||||||
|
bool had_cached_uri = last_uri[0] != '\0';
|
||||||
|
if (had_cached_uri || file_exists(SPOOL_PROBE_STATE_PATH)) {
|
||||||
|
clear_printer_probe_state();
|
||||||
|
if (had_cached_uri) log_msg("cleared printer probe state (no default printer configured)");
|
||||||
|
}
|
||||||
|
last_uri[0] = '\0';
|
||||||
|
*last_probe_ms = 0;
|
||||||
|
*last_probe_ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uri_changed = strcmp(last_uri, current_uri) != 0;
|
||||||
|
uint64_t refresh_ms = *last_probe_ok ? PROBE_REFRESH_OK_MS : PROBE_REFRESH_FAIL_MS;
|
||||||
|
if (!force && !uri_changed && *last_probe_ms != 0 && now - *last_probe_ms < refresh_ms)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ProbeState state = {};
|
||||||
|
char err[128] = {};
|
||||||
|
bool ok = probe_printer_uri(current_uri, &state, err, sizeof(err));
|
||||||
|
if (!save_printer_probe_state(&state))
|
||||||
|
log_msg("warning: failed to persist printer probe state for %s", current_uri);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
if (state.caps.printer_name[0])
|
||||||
|
log_msg("auto-probed printer %s (%s)", state.printer_uri, state.caps.printer_name);
|
||||||
|
else
|
||||||
|
log_msg("auto-probed printer %s", state.printer_uri);
|
||||||
|
if (state.detail[0]) log_msg("probe detail: %s", state.detail);
|
||||||
|
} else {
|
||||||
|
log_msg("auto-probe failed for %s: %s",
|
||||||
|
state.printer_uri[0] ? state.printer_uri : current_uri,
|
||||||
|
state.message[0] ? state.message : (err[0] ? err : "probe failed"));
|
||||||
|
if (state.detail[0]) log_msg("probe detail: %s", state.detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_copy(last_uri, last_uri_len, state.printer_uri[0] ? state.printer_uri : current_uri);
|
||||||
|
*last_probe_ms = now;
|
||||||
|
*last_probe_ok = ok;
|
||||||
|
}
|
||||||
|
|
||||||
static void requeue_stale_active_jobs() {
|
static void requeue_stale_active_jobs() {
|
||||||
DIR* dir = opendir(SPOOL_ACTIVE_DIR);
|
DIR* dir = opendir(SPOOL_ACTIVE_DIR);
|
||||||
if (!dir) return;
|
if (!dir) return;
|
||||||
@@ -260,9 +313,16 @@ extern "C" void _start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
requeue_stale_active_jobs();
|
requeue_stale_active_jobs();
|
||||||
|
char last_probed_uri[MAX_PATH_LEN] = {};
|
||||||
|
uint64_t last_probe_ms = 0;
|
||||||
|
bool last_probe_ok = false;
|
||||||
|
auto_probe_configured_printer(last_probed_uri, sizeof(last_probed_uri),
|
||||||
|
&last_probe_ms, &last_probe_ok, true);
|
||||||
log_msg("ready");
|
log_msg("ready");
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
auto_probe_configured_printer(last_probed_uri, sizeof(last_probed_uri),
|
||||||
|
&last_probe_ms, &last_probe_ok);
|
||||||
char active_path[MAX_PATH_LEN];
|
char active_path[MAX_PATH_LEN];
|
||||||
if (claim_next_job(active_path, sizeof(active_path))) {
|
if (claim_next_job(active_path, sizeof(active_path))) {
|
||||||
process_job(active_path);
|
process_job(active_path);
|
||||||
|
|||||||
+583
-176
File diff suppressed because it is too large
Load Diff
@@ -55,6 +55,8 @@ ICONS=(
|
|||||||
"places/scalable/user-home.svg"
|
"places/scalable/user-home.svg"
|
||||||
"devices/scalable/computer.svg"
|
"devices/scalable/computer.svg"
|
||||||
"devices/scalable/network-wired.svg"
|
"devices/scalable/network-wired.svg"
|
||||||
|
"devices/scalable/printer.svg"
|
||||||
|
"devices/symbolic/printer-symbolic.svg"
|
||||||
"mimetypes/scalable/text-x-generic.svg"
|
"mimetypes/scalable/text-x-generic.svg"
|
||||||
"mimetypes/scalable/application-x-executable.svg"
|
"mimetypes/scalable/application-x-executable.svg"
|
||||||
"categories/scalable/help-about.svg"
|
"categories/scalable/help-about.svg"
|
||||||
|
|||||||
Reference in New Issue
Block a user