190 lines
6.0 KiB
C++
190 lines
6.0 KiB
C++
/*
|
|
* main.cpp
|
|
* libdialogs entry point for shared modal dialogs
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include <montauk/syscall.h>
|
|
#include <montauk/string.h>
|
|
#include <montauk/heap.h>
|
|
#include <gui/gui.hpp>
|
|
#include <gui/standalone.hpp>
|
|
#include <gui/font.hpp>
|
|
#include <gui/dialogs.hpp>
|
|
|
|
#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;
|
|
}
|