/* * dialogs.hpp * Shared dialog request/result structures and client helpers * Copyright (c) 2026 Daniel Hammer */ #pragma once #include #include #ifndef GUI_DIALOGS_LIBRARY_BUILD #include #endif namespace gui::dialogs { inline constexpr const char* LIBRARY_PATH = "0:/os/dialogs.lib"; inline constexpr const char* RUN_REQUEST_SYMBOL = "dialogs_run_request"; inline constexpr const char* MESSAGE_BOX_SYMBOL = "dialogs_message_box"; enum RequestKind : uint8_t { REQUEST_KIND_FILE = 1, REQUEST_KIND_PRINT = 2, }; enum FileDialogMode : uint8_t { FILE_DIALOG_OPEN = 1, FILE_DIALOG_SAVE = 2, }; enum PrintDialogMode : uint8_t { PRINT_DIALOG_SETUP = 1, PRINT_DIALOG_SUBMIT = 2, }; enum MessageBoxButtons : uint8_t { MESSAGE_BOX_OK = 1, MESSAGE_BOX_OK_CANCEL = 2, MESSAGE_BOX_YES_NO = 3, MESSAGE_BOX_YES_NO_CANCEL = 4, }; enum MessageBoxResult : uint8_t { MESSAGE_BOX_RESULT_NONE = 0, MESSAGE_BOX_RESULT_OK = 1, MESSAGE_BOX_RESULT_CANCEL = 2, MESSAGE_BOX_RESULT_YES = 3, MESSAGE_BOX_RESULT_NO = 4, }; struct Request { uint8_t kind; uint8_t mode; char title[96]; char initial_path[256]; char suggested_name[128]; char source_path[256]; char job_name[128]; char printer_uri[256]; uint32_t copies; }; struct Result { char status[16]; char path[256]; char job_id[64]; char printer_uri[256]; char printer_name[128]; uint32_t copies; char message[160]; }; inline void safe_copy(char* dst, int dst_len, const char* src) { if (!dst || dst_len <= 0) return; if (!src) src = ""; montauk::strncpy(dst, src, dst_len - 1); dst[dst_len - 1] = '\0'; } inline void reset_request(Request* req) { if (!req) return; montauk::memset(req, 0, sizeof(Request)); } inline void reset_result(Result* res) { if (!res) return; montauk::memset(res, 0, sizeof(Result)); } #ifndef GUI_DIALOGS_LIBRARY_BUILD using RunRequestFn = bool (*)(const Request*, Result*); using MessageBoxFn = uint8_t (*)(const char*, const char*, uint8_t); struct Runtime { LibHandle* handle; RunRequestFn run_request; MessageBoxFn message_box; }; inline Runtime g_runtime = {}; inline bool ensure_handle(char* out_message, int out_message_len) { if (g_runtime.handle) return true; g_runtime.handle = libloader::dlopen(LIBRARY_PATH); if (!g_runtime.handle) { if (out_message && out_message_len > 0) safe_copy(out_message, out_message_len, "failed to load dialog library"); return false; } return true; } inline RunRequestFn resolve_run_request(char* out_message, int out_message_len) { if (g_runtime.run_request) return g_runtime.run_request; if (!ensure_handle(out_message, out_message_len)) return nullptr; g_runtime.run_request = (RunRequestFn)libloader::dlsym(g_runtime.handle, RUN_REQUEST_SYMBOL); if (!g_runtime.run_request && out_message && out_message_len > 0) safe_copy(out_message, out_message_len, "failed to resolve dialog entry point"); return g_runtime.run_request; } inline MessageBoxFn resolve_message_box(char* out_message, int out_message_len) { if (g_runtime.message_box) return g_runtime.message_box; if (!ensure_handle(out_message, out_message_len)) return nullptr; g_runtime.message_box = (MessageBoxFn)libloader::dlsym(g_runtime.handle, MESSAGE_BOX_SYMBOL); if (!g_runtime.message_box && out_message && out_message_len > 0) safe_copy(out_message, out_message_len, "failed to resolve message box entry point"); return g_runtime.message_box; } inline bool run_request(const Request* req, Result& res, char* out_message, int out_message_len) { if (out_message && out_message_len > 0) out_message[0] = '\0'; if (!req) { if (out_message && out_message_len > 0) safe_copy(out_message, out_message_len, "invalid dialog request"); return false; } RunRequestFn run_fn = resolve_run_request(out_message, out_message_len); if (!run_fn) return false; if (!run_fn(req, &res)) { if (out_message && out_message_len > 0 && !out_message[0]) safe_copy(out_message, out_message_len, "dialog library invocation failed"); return false; } if (out_message && out_message_len > 0) safe_copy(out_message, out_message_len, res.message); return montauk::streq(res.status, "ok"); } inline bool open_file(const char* title, const char* initial_path, char* out_path, int out_path_len, char* out_message = nullptr, int out_message_len = 0) { Request req = {}; req.kind = REQUEST_KIND_FILE; req.mode = FILE_DIALOG_OPEN; safe_copy(req.title, sizeof(req.title), title ? title : "Open"); safe_copy(req.initial_path, sizeof(req.initial_path), initial_path); Result res = {}; bool ok = run_request(&req, res, out_message, out_message_len); if (ok && out_path && out_path_len > 0) safe_copy(out_path, out_path_len, res.path); return ok; } inline bool save_file(const char* title, const char* initial_path, const char* suggested_name, char* out_path, int out_path_len, char* out_message = nullptr, int out_message_len = 0) { Request req = {}; req.kind = REQUEST_KIND_FILE; req.mode = FILE_DIALOG_SAVE; safe_copy(req.title, sizeof(req.title), title ? title : "Save"); safe_copy(req.initial_path, sizeof(req.initial_path), initial_path); safe_copy(req.suggested_name, sizeof(req.suggested_name), suggested_name); Result res = {}; bool ok = run_request(&req, res, out_message, out_message_len); if (ok && out_path && out_path_len > 0) safe_copy(out_path, out_path_len, res.path); return ok; } inline bool print_file(const char* title, const char* source_path, const char* job_name, char* out_job_id, int out_job_id_len, char* out_message = nullptr, int out_message_len = 0) { Request req = {}; req.kind = REQUEST_KIND_PRINT; req.mode = PRINT_DIALOG_SUBMIT; req.copies = 1; safe_copy(req.title, sizeof(req.title), title ? title : "Print"); safe_copy(req.source_path, sizeof(req.source_path), source_path); safe_copy(req.job_name, sizeof(req.job_name), job_name); Result res = {}; bool ok = run_request(&req, res, out_message, out_message_len); if (ok && out_job_id && out_job_id_len > 0) safe_copy(out_job_id, out_job_id_len, res.job_id); return ok; } inline bool configure_print(const char* title, const char* initial_printer_uri, const char* job_name, char* out_printer_uri, int out_printer_uri_len, char* out_printer_name, int out_printer_name_len, uint32_t* out_copies = nullptr, char* out_message = nullptr, int out_message_len = 0) { Request req = {}; req.kind = REQUEST_KIND_PRINT; req.mode = PRINT_DIALOG_SETUP; req.copies = out_copies && *out_copies > 0 ? *out_copies : 1; safe_copy(req.title, sizeof(req.title), title ? title : "Print"); safe_copy(req.job_name, sizeof(req.job_name), job_name ? job_name : ""); safe_copy(req.printer_uri, sizeof(req.printer_uri), initial_printer_uri); Result res = {}; bool ok = run_request(&req, res, out_message, out_message_len); if (ok && out_printer_uri && out_printer_uri_len > 0) safe_copy(out_printer_uri, out_printer_uri_len, res.printer_uri); if (ok && out_printer_name && out_printer_name_len > 0) safe_copy(out_printer_name, out_printer_name_len, res.printer_name); if (ok && out_copies) *out_copies = res.copies > 0 ? res.copies : 1; return ok; } inline MessageBoxResult message_box(const char* title, const char* message, MessageBoxButtons buttons = MESSAGE_BOX_OK, char* out_message = nullptr, int out_message_len = 0) { if (out_message && out_message_len > 0) out_message[0] = '\0'; MessageBoxFn fn = resolve_message_box(out_message, out_message_len); if (!fn) return MESSAGE_BOX_RESULT_NONE; uint8_t result = fn(title ? title : "Message", message ? message : "", (uint8_t)buttons); if (result == MESSAGE_BOX_RESULT_NONE && out_message && out_message_len > 0 && !out_message[0]) safe_copy(out_message, out_message_len, "message box invocation failed"); return (MessageBoxResult)result; } #endif } // namespace gui::dialogs