309 lines
9.8 KiB
C++
309 lines
9.8 KiB
C++
/*
|
|
* diskdetail.cpp
|
|
* Disk detail popup window
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include "px.h"
|
|
|
|
// ============================================================================
|
|
// Helpers
|
|
// ============================================================================
|
|
|
|
static void fmt_u64(char* buf, int bufsize, uint64_t v) {
|
|
if (v == 0) { buf[0] = '0'; buf[1] = '\0'; return; }
|
|
char tmp[24]; int i = 0;
|
|
while (v > 0) { tmp[i++] = '0' + (int)(v % 10); v /= 10; }
|
|
int j = 0;
|
|
while (i > 0 && j < bufsize - 1) buf[j++] = tmp[--i];
|
|
buf[j] = '\0';
|
|
}
|
|
|
|
static void dd_table_row(uint32_t* px, int bw, int bh,
|
|
int x, int y, int col1_w, int row_h,
|
|
const char* key, const char* value,
|
|
Color key_col, Color val_col) {
|
|
int fh = font_h();
|
|
int ty = y + (row_h - fh) / 2;
|
|
px_text(px, bw, bh, x + 8, ty, key, key_col);
|
|
px_text(px, bw, bh, x + col1_w + 8, ty, value, val_col);
|
|
}
|
|
|
|
static void dd_feature_row(uint32_t* px, int bw, int bh,
|
|
int x, int y, int w, int row_h,
|
|
const char* label, bool supported, const char* extra) {
|
|
int fh = font_h();
|
|
int ty = y + (row_h - fh) / 2;
|
|
|
|
int dot_y = y + (row_h - 8) / 2;
|
|
Color dot_col = supported ? Color::from_rgb(0x22, 0x88, 0x22)
|
|
: Color::from_rgb(0xBB, 0xBB, 0xBB);
|
|
px_fill_rounded(px, bw, bh, x + 10, dot_y, 8, 8, 4, dot_col);
|
|
|
|
Color text_col = supported ? TEXT_COLOR : Color::from_rgb(0xAA, 0xAA, 0xAA);
|
|
px_text(px, bw, bh, x + 26, ty, label, text_col);
|
|
|
|
if (extra && extra[0]) {
|
|
int ew = text_w(extra);
|
|
px_text(px, bw, bh, x + w - ew - 12, ty, extra, DIM_TEXT);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tab content: General
|
|
// ============================================================================
|
|
|
|
static void dd_draw_general(uint32_t* px, int bw, int bh, DiskDetailState* dd) {
|
|
char line[128];
|
|
int fh = font_h();
|
|
int x = 0;
|
|
int y = DD_TAB_BAR_H + 12;
|
|
int w = bw;
|
|
int col1_w = 110;
|
|
int row_h = fh + 8;
|
|
Color key_col = DIM_TEXT;
|
|
Color val_col = TEXT_COLOR;
|
|
Color hdr_col = ACCENT_COLOR;
|
|
Color border = Color::from_rgb(0xE0, 0xE0, 0xE0);
|
|
|
|
// Identification section
|
|
px_text(px, bw, bh, x + 8, y, "Identification", hdr_col);
|
|
y += fh + 8;
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 1;
|
|
|
|
int rowIdx = 0;
|
|
auto table_row = [&](const char* key, const char* val) {
|
|
if (rowIdx++ % 2 == 0)
|
|
px_fill(px, bw, bh, x + 8, y, w - 16, row_h,
|
|
Color::from_rgb(0xF7, 0xF7, 0xF7));
|
|
dd_table_row(px, bw, bh, x, y, col1_w, row_h, key, val, key_col, val_col);
|
|
y += row_h;
|
|
};
|
|
|
|
table_row("Model", dd->info.model);
|
|
table_row("Serial", dd->info.serial);
|
|
table_row("Firmware", dd->info.firmware);
|
|
|
|
const char* typeStr = "Unknown";
|
|
if (dd->info.type == 1) typeStr = "SATA";
|
|
else if (dd->info.type == 2) typeStr = "SATAPI";
|
|
else if (dd->info.type == 3) typeStr = "NVMe";
|
|
else if (dd->info.type == 4) typeStr = "USB Mass Storage";
|
|
table_row("Type", typeStr);
|
|
|
|
snprintf(line, sizeof(line), "%d", (int)dd->info.port);
|
|
table_row("Device Index", line);
|
|
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 12;
|
|
|
|
// Capacity section
|
|
px_text(px, bw, bh, x + 8, y, "Capacity", hdr_col);
|
|
y += fh + 8;
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 1;
|
|
|
|
rowIdx = 0;
|
|
uint64_t totalBytes = dd->info.sectorCount * (uint64_t)dd->info.sectorSizeLog;
|
|
uint64_t totalMB = totalBytes / (1024 * 1024);
|
|
uint64_t totalGB = totalMB / 1024;
|
|
if (totalGB > 0) {
|
|
int fracGB = (int)((totalMB % 1024) * 10 / 1024);
|
|
snprintf(line, sizeof(line), "%d.%d GiB", (int)totalGB, fracGB);
|
|
} else {
|
|
snprintf(line, sizeof(line), "%d MiB", (int)totalMB);
|
|
}
|
|
table_row("Size", line);
|
|
|
|
char secbuf[24];
|
|
fmt_u64(secbuf, sizeof(secbuf), dd->info.sectorCount);
|
|
table_row("Sectors", secbuf);
|
|
|
|
snprintf(line, sizeof(line), "%d bytes", (int)dd->info.sectorSizeLog);
|
|
table_row("Logical", line);
|
|
|
|
snprintf(line, sizeof(line), "%d bytes", (int)dd->info.sectorSizePhys);
|
|
table_row("Physical", line);
|
|
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 12;
|
|
|
|
// Interface section
|
|
px_text(px, bw, bh, x + 8, y, "Interface", hdr_col);
|
|
y += fh + 8;
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 1;
|
|
|
|
rowIdx = 0;
|
|
const char* sataSpeed = "Unknown";
|
|
if (dd->info.sataGen == 1) sataSpeed = "SATA I (1.5 Gb/s)";
|
|
else if (dd->info.sataGen == 2) sataSpeed = "SATA II (3.0 Gb/s)";
|
|
else if (dd->info.sataGen == 3) sataSpeed = "SATA III (6.0 Gb/s)";
|
|
table_row("Link Speed", sataSpeed);
|
|
|
|
if (dd->info.rpm == 0)
|
|
table_row("Media", "Not reported");
|
|
else if (dd->info.rpm == 1)
|
|
table_row("Media", "Solid State (SSD)");
|
|
else {
|
|
snprintf(line, sizeof(line), "%d RPM", (int)dd->info.rpm);
|
|
table_row("Media", line);
|
|
}
|
|
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Tab content: Features
|
|
// ============================================================================
|
|
|
|
static void dd_draw_features(uint32_t* px, int bw, int bh, DiskDetailState* dd) {
|
|
char line[64];
|
|
int fh = font_h();
|
|
int x = 0;
|
|
int y = DD_TAB_BAR_H + 12;
|
|
int w = bw;
|
|
int row_h = fh + 10;
|
|
Color hdr_col = ACCENT_COLOR;
|
|
Color border = Color::from_rgb(0xE0, 0xE0, 0xE0);
|
|
|
|
px_text(px, bw, bh, x + 8, y, "Supported Features", hdr_col);
|
|
y += fh + 8;
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 4;
|
|
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "48-bit LBA", dd->info.supportsLba48, nullptr);
|
|
y += row_h;
|
|
|
|
if (dd->info.supportsNcq) {
|
|
snprintf(line, sizeof(line), "Depth: %d", (int)dd->info.ncqDepth);
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "Native Command Queuing (NCQ)", true, line);
|
|
} else {
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "Native Command Queuing (NCQ)", false, nullptr);
|
|
}
|
|
y += row_h;
|
|
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "TRIM (Data Set Management)", dd->info.supportsTrim, nullptr);
|
|
y += row_h;
|
|
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "S.M.A.R.T.", dd->info.supportsSmart, nullptr);
|
|
y += row_h;
|
|
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "Write Cache", dd->info.supportsWriteCache, nullptr);
|
|
y += row_h;
|
|
|
|
dd_feature_row(px, bw, bh, x, y, w, row_h, "Read Look-Ahead", dd->info.supportsReadAhead, nullptr);
|
|
y += row_h;
|
|
|
|
y += 4;
|
|
px_hline(px, bw, bh, x + 8, y, w - 16, border);
|
|
y += 12;
|
|
|
|
// Legend
|
|
Color legend_col = Color::from_rgb(0x88, 0x88, 0x88);
|
|
int ly = y + (fh - 8) / 2;
|
|
px_fill_rounded(px, bw, bh, x + 10, ly, 8, 8, 4, Color::from_rgb(0x22, 0x88, 0x22));
|
|
px_text(px, bw, bh, x + 26, y, "Supported", legend_col);
|
|
|
|
px_fill_rounded(px, bw, bh, x + 120, ly, 8, 8, 4, Color::from_rgb(0xBB, 0xBB, 0xBB));
|
|
px_text(px, bw, bh, x + 136, y, "Not supported", legend_col);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Render
|
|
// ============================================================================
|
|
|
|
void render_disk_detail() {
|
|
auto& dd = g_state.detail;
|
|
if (!dd.open) return;
|
|
|
|
uint32_t* px = dd.pixels;
|
|
int bw = dd.win_w, bh = dd.win_h;
|
|
int fh = font_h();
|
|
|
|
px_fill(px, bw, bh, 0, 0, bw, bh, BG_COLOR);
|
|
|
|
// Tab bar
|
|
px_fill(px, bw, bh, 0, 0, bw, DD_TAB_BAR_H, TOOLBAR_BG);
|
|
px_hline(px, bw, bh, 0, DD_TAB_BAR_H - 1, bw, BORDER_COLOR);
|
|
|
|
int tab_w = bw / DD_TAB_COUNT;
|
|
for (int i = 0; i < DD_TAB_COUNT; i++) {
|
|
int tx = i * tab_w;
|
|
bool active = (i == dd.active_tab);
|
|
|
|
if (active) {
|
|
px_fill(px, bw, bh, tx, 0, tab_w, DD_TAB_BAR_H, BG_COLOR);
|
|
px_fill(px, bw, bh, tx + 4, DD_TAB_BAR_H - 3, tab_w - 8, 3, ACCENT_COLOR);
|
|
}
|
|
|
|
int tw = text_w(dd_tab_labels[i]);
|
|
Color tc = active ? ACCENT_COLOR : DIM_TEXT;
|
|
px_text(px, bw, bh, tx + (tab_w - tw) / 2, (DD_TAB_BAR_H - fh) / 2,
|
|
dd_tab_labels[i], tc);
|
|
}
|
|
|
|
// Tab content
|
|
switch (dd.active_tab) {
|
|
case 0: dd_draw_general(px, bw, bh, &dd); break;
|
|
case 1: dd_draw_features(px, bw, bh, &dd); break;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Window management
|
|
// ============================================================================
|
|
|
|
void open_disk_detail(int port, const char* model) {
|
|
auto& dd = g_state.detail;
|
|
|
|
if (dd.open) close_disk_detail();
|
|
|
|
char title[64];
|
|
int i = 0;
|
|
const char* prefix = "Disk: ";
|
|
while (prefix[i]) { title[i] = prefix[i]; i++; }
|
|
int j = 0;
|
|
while (model[j] && i < 62) { title[i++] = model[j++]; }
|
|
title[i] = '\0';
|
|
|
|
dd.win_w = DD_INIT_W;
|
|
dd.win_h = DD_INIT_H;
|
|
dd.active_tab = 0;
|
|
montauk::memset(&dd.info, 0, sizeof(dd.info));
|
|
montauk::diskinfo(&dd.info, port);
|
|
|
|
Montauk::WinCreateResult wres;
|
|
if (montauk::win_create(title, dd.win_w, dd.win_h, &wres) < 0 || wres.id < 0)
|
|
return;
|
|
|
|
dd.win_id = wres.id;
|
|
dd.pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
|
|
dd.open = true;
|
|
|
|
render_disk_detail();
|
|
montauk::win_present(dd.win_id);
|
|
}
|
|
|
|
void close_disk_detail() {
|
|
auto& dd = g_state.detail;
|
|
if (!dd.open) return;
|
|
montauk::win_destroy(dd.win_id);
|
|
dd.open = false;
|
|
}
|
|
|
|
bool handle_detail_mouse(int mx, int my, bool clicked) {
|
|
auto& dd = g_state.detail;
|
|
if (!clicked) return false;
|
|
|
|
if (my >= 0 && my < DD_TAB_BAR_H) {
|
|
int tab_w = dd.win_w / DD_TAB_COUNT;
|
|
int tab = mx / tab_w;
|
|
if (tab >= 0 && tab < DD_TAB_COUNT)
|
|
dd.active_tab = tab;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|