/* * main.cpp * libdialogs entry point for shared modal dialogs * Copyright (c) 2026 Daniel Hammer */ #include #include #include #include #include #include #include #include "main.hpp" #include "filedialog.hpp" #include "printdialog.hpp" #include "messagebox.hpp" using namespace gui; namespace dlg = gui::dialogs; #define DIALOGS_EXPORT extern "C" __attribute__((visibility("default"))) AppState g_app = {}; static bool g_fonts_ready = false; // WinEvent::type values (from Api/Syscall.hpp) static constexpr uint8_t WIN_KEY = 0; static constexpr uint8_t WIN_MOUSE = 1; static constexpr uint8_t WIN_RESIZE = 2; static constexpr uint8_t WIN_CLOSE = 3; static constexpr uint8_t WIN_SCALE = 4; struct DialogCallbacks { void (*draw)(); void (*on_close)(); void (*handle_mouse)(const montauk::abi::WinEvent& ev); void (*handle_key)(const montauk::abi::KeyEvent& key); }; static void run_dialog_loop(const DialogCallbacks& cb) { while (g_app.running) { cb.draw(); g_app.win.present(); montauk::abi::WinEvent ev; int rc = g_app.win.poll(&ev); if (rc < 0) { cb.on_close(); break; } if (rc == 0) { montauk::sleep_ms(16); continue; } if (ev.type == WIN_CLOSE) { cb.on_close(); break; } if (ev.type == WIN_MOUSE) { cb.handle_mouse(ev); continue; } if (ev.type == WIN_KEY) { cb.handle_key(ev.key); } // WIN_RESIZE and WIN_SCALE are handled inside WsWindow::poll } } static void cancel_and_stop() { dialog_cancel(""); } void dialog_finish(const char* status, const char* path, const char* job_id, const char* message, const char* printer_uri, const char* printer_name, uint32_t copies) { gui::dialogs::safe_copy(g_app.result.status, sizeof(g_app.result.status), status); gui::dialogs::safe_copy(g_app.result.path, sizeof(g_app.result.path), path); gui::dialogs::safe_copy(g_app.result.job_id, sizeof(g_app.result.job_id), job_id); gui::dialogs::safe_copy(g_app.result.printer_uri, sizeof(g_app.result.printer_uri), printer_uri); gui::dialogs::safe_copy(g_app.result.printer_name, sizeof(g_app.result.printer_name), printer_name); g_app.result.copies = copies; gui::dialogs::safe_copy(g_app.result.message, sizeof(g_app.result.message), message); g_app.running = false; } void dialog_cancel(const char* message) { dialog_finish("cancel", "", "", message, "", "", 0); } void cleanup() { if (g_app.is_print) { printdialog::cleanup(); } else if (g_app.is_message) { messagebox::cleanup(); } else { filedialog::cleanup(); } g_app.win.destroy(); } static void set_error_result(dlg::Result* result, const char* message) { if (!result) return; dlg::reset_result(result); dlg::safe_copy(result->status, sizeof(result->status), "error"); dlg::safe_copy(result->message, sizeof(result->message), message); } static bool ensure_fonts(dlg::Result* result) { if (g_fonts_ready) return true; if (!fonts::init()) { if (result) set_error_result(result, "failed to initialize dialog fonts"); return false; } g_fonts_ready = true; return true; } DIALOGS_EXPORT bool dialogs_run_request(const dlg::Request* request, dlg::Result* out_result) { if (!request || !out_result) return false; set_error_result(out_result, "dialog failed"); if (!ensure_fonts(out_result)) return true; if (request->kind != dlg::REQUEST_KIND_FILE && request->kind != dlg::REQUEST_KIND_PRINT) { set_error_result(out_result, "unsupported dialog request"); return true; } g_app = AppState {}; g_app.request = *request; g_app.running = true; dlg::reset_result(&g_app.result); g_app.is_print = g_app.request.kind == dlg::REQUEST_KIND_PRINT; int init_w = filedialog::FILE_INIT_W; int init_h = filedialog::FILE_INIT_H; if (g_app.is_print) { init_w = printdialog::PRINT_INIT_W; init_h = printdialog::PRINT_INIT_H; } const char* default_title = g_app.is_print ? "Print" : "Open"; const char* title = g_app.request.title[0] ? g_app.request.title : default_title; if (g_app.is_print) printdialog::init(g_app.request); else filedialog::init(g_app.request); if (!g_app.win.create(title, init_w, init_h)) { cleanup(); set_error_result(out_result, "failed to create dialog window"); return true; } void (*draw_fn)() = g_app.is_print ? printdialog::draw : filedialog::draw; void (*mouse_fn)(const montauk::abi::WinEvent&) = g_app.is_print ? printdialog::handle_mouse : filedialog::handle_mouse; void (*key_fn)(const montauk::abi::KeyEvent&) = g_app.is_print ? printdialog::handle_key : filedialog::handle_key; draw_fn(); g_app.win.present(); run_dialog_loop({ draw_fn, cancel_and_stop, mouse_fn, key_fn }); cleanup(); *out_result = g_app.result; return true; } DIALOGS_EXPORT uint8_t dialogs_message_box(const char* title, const char* message, uint8_t buttons) { if (!ensure_fonts(nullptr)) return dlg::MESSAGE_BOX_RESULT_NONE; g_app = AppState {}; g_app.running = true; g_app.is_message = true; const char* window_title = title && title[0] ? title : "Message"; messagebox::init(message ? message : "", buttons); int init_w = 0; int init_h = 0; messagebox::preferred_size(window_title, &init_w, &init_h); if (!g_app.win.create(window_title, init_w, init_h)) { cleanup(); return dlg::MESSAGE_BOX_RESULT_NONE; } messagebox::draw(); g_app.win.present(); run_dialog_loop({ messagebox::draw, messagebox::dismiss, messagebox::handle_mouse, messagebox::handle_key }); uint8_t choice = messagebox::selected_choice(); cleanup(); return choice ? choice : (uint8_t)dlg::MESSAGE_BOX_RESULT_NONE; }