lardon3d/src/layout.c
2026-08-06 21:05:02 +02:00

564 lines
15 KiB
C

#include <ncurses.h>
#include <stdio.h>
#include <string.h>
#include <lardon3d/layout.h>
#include <lardon3d/image_catalog.h>
#include <lardon3d/image_view.h>
enum {
MINIMUM_ROWS = 20,
MINIMUM_COLUMNS = 72,
};
static void
draw_text(int row, int column, int available, const char *text)
{
if (available <= 0) {
return;
}
size_t length = strlen(text);
int displayed = length < (size_t)available ? (int)length : available;
(void)mvaddnstr(row, column, text, displayed);
}
static void
draw_too_small(int rows, int columns)
{
static const char message[] = "Terminal trop petit";
int row = rows > 0 ? rows / 2 : 0;
int column = columns > (int)(sizeof(message) - 1)
? (columns - (int)(sizeof(message) - 1)) / 2
: 0;
draw_text(row, column, columns, message);
}
static void
draw_frame(int rows, int columns)
{
int journal_row = rows - 7;
int footer_row = rows - 3;
(void)box(stdscr, 0, 0);
(void)mvhline(2, 1, ACS_HLINE, columns - 2);
(void)mvhline(journal_row, 1, ACS_HLINE, columns - 2);
(void)mvhline(footer_row, 1, ACS_HLINE, columns - 2);
(void)mvaddch(2, 0, ACS_LTEE);
(void)mvaddch(2, columns - 1, ACS_RTEE);
(void)mvaddch(journal_row, 0, ACS_LTEE);
(void)mvaddch(journal_row, columns - 1, ACS_RTEE);
(void)mvaddch(footer_row, 0, ACS_LTEE);
(void)mvaddch(footer_row, columns - 1, ACS_RTEE);
}
static void
screen_texts(
Lardon3DScreen screen,
const char **title,
const char **content,
const char **footer
)
{
*footer = "ESC Accueil F1 Aide F2 Projets F3 Import F4 Viewer F5 Tâches F6 Ressources Q";
switch (screen) {
case LARDON3D_SCREEN_PROJECTS:
*title = "Projets";
*content = "Gestion des projets";
*footer = "N Nouveau O Ouvrir C Fermer ESC Accueil Q Quit";
break;
case LARDON3D_SCREEN_IMPORT:
*title = "Import";
*content = "Import des images";
*footer = "I Importer R Recharger S Tri / Filtre X Effacer ↑/↓ ESC Q";
break;
case LARDON3D_SCREEN_VIEWER:
*title = "Viewer";
*content = "Le viewer Vulkan n'est pas encore disponible.";
break;
case LARDON3D_SCREEN_HELP:
*title = "Aide";
*content = "Raccourcis clavier.";
break;
case LARDON3D_SCREEN_TASKS:
*title = "Tâches";
*content = "Tâches en arrière-plan";
*footer = "F5 Tâches ESC Accueil Q Quit";
break;
case LARDON3D_SCREEN_RESOURCES:
*title = "Ressources";
*content = "Gouverneur de ressources";
*footer = "F6 Ressources ESC Accueil Q Quit";
break;
case LARDON3D_SCREEN_HOME:
default:
*title = "Accueil";
*content = "Bienvenue dans Lardon3D";
*footer = "F1 Aide F2 Projets F3 Import F4 Viewer F5 Tâches F6 Ressources Q";
break;
}
}
static void
draw_input_field(
const char *input_text,
const char *input_label,
int row,
int columns
)
{
if (!input_text) {
return;
}
draw_text(row, 4, columns - 6, input_label);
(void)mvaddch(row + 1, 2, '[');
(void)mvaddch(row + 1, columns - 3, ']');
int available = columns - 8;
size_t length = strlen(input_text);
const char *visible = input_text;
if (length > (size_t)available) {
visible += length - (size_t)available;
length = (size_t)available;
}
draw_text(row + 1, 4, available, visible);
(void)move(row + 1, 4 + (int)length);
}
static void
draw_project_screen(
const char *input_text,
const char *input_label,
int columns
)
{
draw_text(6, 4, columns - 6, "N : Nouveau projet");
draw_text(7, 4, columns - 6, "O : Ouvrir un projet");
draw_text(8, 4, columns - 6, "C : Fermer le projet");
draw_text(9, 4, columns - 6, "ESC : Accueil");
draw_text(10, 4, columns - 6, "Q : Quitter");
draw_input_field(input_text, input_label, 11, columns);
}
static void
draw_catalog(
const Lardon3DAppState *state,
int rows,
int columns
)
{
if (!state->project_loaded) {
draw_text(7, 4, columns - 6, "Aucun projet chargé.");
return;
}
size_t count = lardon3d_image_view_count(state->image_view);
size_t total = lardon3d_image_catalog_count(state->image_catalog);
char line[512];
(void)snprintf(
line,
sizeof(line),
"Images visibles : %zu / %zu",
count,
total
);
draw_text(6, 4, columns - 6, line);
char size_text[64];
lardon3d_image_catalog_format_size(
lardon3d_image_view_total_size(state->image_view),
size_text,
sizeof(size_text)
);
(void)snprintf(
line,
sizeof(line),
"Taille totale visible : %s",
size_text
);
draw_text(7, 4, columns - 6, line);
(void)snprintf(
line,
sizeof(line),
"Tri : %s",
lardon3d_image_view_sort_name(
lardon3d_image_view_sort(state->image_view)
)
);
draw_text(8, 4, columns - 6, line);
const char *filter = lardon3d_image_view_filter(state->image_view);
(void)snprintf(
line,
sizeof(line),
"Filtre : %s",
filter[0] ? filter : "Aucun"
);
draw_text(9, 4, columns - 6, line);
if (count == 0) {
draw_text(
11,
4,
columns - 6,
filter[0]
? "Aucune image ne correspond au filtre."
: "Aucune image importée."
);
return;
}
int journal_row = rows - 7;
size_t visible = journal_row > 11 ? (size_t)(journal_row - 11) : 0;
size_t offset = lardon3d_image_view_offset(state->image_view);
size_t selection = lardon3d_image_view_selection(state->image_view);
for (size_t row = 0; row < visible; ++row) {
size_t index = offset + row;
const Lardon3DImageEntry *entry = lardon3d_image_view_get(
state->image_view,
index
);
if (!entry) {
break;
}
lardon3d_image_catalog_format_size(
entry->size_bytes,
size_text,
sizeof(size_text)
);
(void)snprintf(
line,
sizeof(line),
"%c %s %s",
index == selection ? '>' : ' ',
entry->filename,
size_text
);
draw_text(11 + (int)row, 4, columns - 6, line);
}
}
static void
draw_import_screen(
const Lardon3DAppState *state,
const char *input_text,
const char *input_label,
const Lardon3DImportTaskSnapshot *snapshot,
int rows,
int columns
)
{
if (snapshot && snapshot->status == LARDON3D_IMPORT_TASK_RUNNING) {
draw_text(6, 4, columns - 6, "Import en cours");
char line[128];
(void)snprintf(
line,
sizeof(line),
"Fichiers : %zu / %zu",
snapshot->processed,
snapshot->total
);
draw_text(7, 4, columns - 6, line);
(void)snprintf(line, sizeof(line), "Copiés : %zu", snapshot->copied);
draw_text(8, 4, columns - 6, line);
(void)snprintf(
line,
sizeof(line),
"Déjà présents : %zu",
snapshot->already_present
);
draw_text(9, 4, columns - 6, line);
(void)snprintf(line, sizeof(line), "Ignorés : %zu", snapshot->ignored);
draw_text(10, 4, columns - 6, line);
int percent = snapshot->total == 0
? 0
: (int)((snapshot->processed * 100) / snapshot->total);
if (percent > 100) {
percent = 100;
}
int bar_width = columns - 18;
if (bar_width > 40) {
bar_width = 40;
}
if (bar_width < 1) {
bar_width = 1;
}
int filled = (bar_width * percent) / 100;
char bar[64];
bar[0] = '[';
for (int index = 0; index < bar_width; ++index) {
bar[index + 1] = index < filled ? '#' : '-';
}
(void)snprintf(
bar + bar_width + 1,
sizeof(bar) - (size_t)bar_width - 1,
"] %d %%",
percent
);
draw_text(11, 4, columns - 6, bar);
draw_text(12, 4, columns - 6, "C : Annuler l'import");
return;
}
if (input_text) {
draw_input_field(input_text, input_label, 7, columns);
return;
}
draw_catalog(state, rows, columns);
}
static void
draw_tasks(
const Lardon3DTaskSnapshot *snapshots,
size_t count,
const Lardon3DTaskQueueSummary *summary,
int rows,
int columns
)
{
char line[512];
(void)snprintf(
line,
sizeof(line),
"Nombre de tâches : %zu En cours : %zu En attente : %zu Terminées : %zu",
summary->total,
summary->running,
summary->pending,
summary->completed
);
draw_text(6, 2, columns - 4, line);
draw_text(8, 2, columns - 4, "ID Nom Etat Progression Message");
int journal_row = rows - 7;
size_t visible = journal_row > 9 ? (size_t)(journal_row - 9) : 0;
if (count == 0) {
draw_text(10, 4, columns - 6, "Aucune tâche.");
return;
}
size_t displayed = count < visible ? count : visible;
for (size_t index = 0; index < displayed; ++index) {
(void)snprintf(
line,
sizeof(line),
"%-6llu %-20.20s %-12.12s %3u %% %s",
(unsigned long long)snapshots[index].id,
snapshots[index].name,
lardon3d_task_state_name(snapshots[index].state),
snapshots[index].progress,
snapshots[index].message
);
draw_text(9 + (int)index, 2, columns - 4, line);
}
}
static void
draw_resource_line(
int row,
int columns,
const char *label,
uint64_t reserved,
uint64_t available
)
{
char reserved_text[64];
char available_text[64];
char line[256];
lardon3d_image_catalog_format_size(
reserved,
reserved_text,
sizeof(reserved_text)
);
lardon3d_image_catalog_format_size(
available,
available_text,
sizeof(available_text)
);
(void)snprintf(
line,
sizeof(line),
"%s réservée : %s Restante : %s",
label,
reserved_text,
available_text
);
draw_text(row, 4, columns - 6, line);
}
static void
draw_resources(
const Lardon3DResourceAvailability *availability,
int columns
)
{
if (!availability) {
draw_text(7, 4, columns - 6, "Ressources système indisponibles.");
return;
}
char line[256];
(void)snprintf(
line,
sizeof(line),
"Réservations actives : %zu",
availability->active_reservations
);
draw_text(6, 4, columns - 6, line);
draw_resource_line(
8,
columns,
"RAM",
availability->memory_reserved_bytes,
availability->memory_available_bytes
);
if (availability->gpu_memory_known) {
draw_resource_line(
9,
columns,
"GPU",
availability->gpu_memory_reserved_bytes,
availability->gpu_memory_available_bytes
);
} else {
draw_text(9, 4, columns - 6, "Mémoire GPU disponible : inconnue");
}
(void)snprintf(
line,
sizeof(line),
"CPU réservés : %u Restants : %u / %u",
availability->cpu_reserved,
availability->cpu_available,
availability->cpu_budget
);
draw_text(10, 4, columns - 6, line);
(void)snprintf(
line,
sizeof(line),
"Slots GPU réservés : %u Restants : %u / %u",
availability->gpu_slots_reserved,
availability->gpu_slots_available,
availability->gpu_slot_budget
);
draw_text(11, 4, columns - 6, line);
(void)snprintf(
line,
sizeof(line),
"Slots IO réservés : %u Restants : %u / %u",
availability->io_slots_reserved,
availability->io_slots_available,
availability->io_slot_budget
);
draw_text(12, 4, columns - 6, line);
}
static void
draw_content(
const Lardon3DAppState *state,
const char *input_text,
const char *input_label,
const Lardon3DImportTaskSnapshot *import_snapshot,
const Lardon3DTaskSnapshot *task_snapshots,
size_t task_count,
const Lardon3DTaskQueueSummary *task_summary,
const Lardon3DResourceAvailability *resource_availability,
int rows,
int columns
)
{
const char *title;
const char *content;
const char *footer;
screen_texts(state->screen, &title, &content, &footer);
bool import_running = import_snapshot
&& import_snapshot->status == LARDON3D_IMPORT_TASK_RUNNING;
if (import_running) {
footer = "C Annuler l'import Q désactivé";
}
int title_length = (int)strlen(title);
int content_length = (int)strlen(content);
int journal_row = rows - 7;
const char *project = state->project_loaded
? state->project_name
: "Aucun projet chargé.";
draw_text(1, (columns - title_length) / 2, title_length, title);
draw_text(3, 2, columns - 4, "Projet");
draw_text(4, 4, columns - 6, project);
if (state->project_loaded) {
draw_text(5, 4, columns - 6, state->project_path);
}
if (state->screen == LARDON3D_SCREEN_PROJECTS) {
draw_project_screen(
input_text,
input_label,
columns
);
} else if (state->screen == LARDON3D_SCREEN_IMPORT) {
draw_import_screen(
state,
input_text,
input_label,
import_snapshot,
rows,
columns
);
} else if (state->screen == LARDON3D_SCREEN_TASKS) {
draw_tasks(
task_snapshots,
task_count,
task_summary,
rows,
columns
);
} else if (state->screen == LARDON3D_SCREEN_RESOURCES) {
draw_resources(resource_availability, columns);
} else {
draw_text(
(3 + journal_row) / 2,
(columns - content_length) / 2,
content_length,
content
);
}
draw_text(journal_row + 1, 2, columns - 4, "Journal");
draw_text(
journal_row + 2,
4,
columns - 6,
import_running ? import_snapshot->message : state->status_message
);
draw_text(rows - 2, 2, columns - 4, footer);
}
void
lardon3d_layout_draw(
const Lardon3DAppState *state,
const char *input_text,
const char *input_label,
const Lardon3DImportTaskSnapshot *import_snapshot,
const Lardon3DTaskSnapshot *task_snapshots,
size_t task_count,
const Lardon3DTaskQueueSummary *task_summary,
const Lardon3DResourceAvailability *resource_availability,
int rows,
int columns
)
{
(void)erase();
if (rows < MINIMUM_ROWS || columns < MINIMUM_COLUMNS) {
draw_too_small(rows, columns);
} else {
draw_frame(rows, columns);
draw_content(
state,
input_text,
input_label,
import_snapshot,
task_snapshots,
task_count,
task_summary,
resource_availability,
rows,
columns
);
}
(void)refresh();
}