249 lines
8.6 KiB
C++
249 lines
8.6 KiB
C++
/*
|
|
* dialogs.hpp
|
|
* Shared dialog request/result structures and client helpers
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <montauk/string.h>
|
|
|
|
#ifndef GUI_DIALOGS_LIBRARY_BUILD
|
|
#include <libloader/libloader.h>
|
|
#endif
|
|
|
|
namespace gui::dialogs {
|
|
|
|
inline constexpr const char* LIBRARY_PATH = "0:/os/libdialogs.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
|