smokes: add curated LLVM runner; archive legacy smokes; PHI-off unified across Bridge/Builder; LLVM resolver tracing; minimal Throw lowering; config env getters; dev profile and root cleaner; docs updated; CI workflow runs curated LLVM (PHI-on/off)
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
// Nyash Explorer with Icons - Windows API Drive Icon Viewer
|
||||
// エクスプローラー風ドライブアイコン付きビューアー
|
||||
|
||||
use eframe::egui::{self, FontFamily, ColorImage, TextureHandle};
|
||||
use eframe::egui::{self, ColorImage, FontFamily, TextureHandle};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
// use std::collections::HashMap;
|
||||
@ -10,12 +10,7 @@ use std::io::Read;
|
||||
#[cfg(windows)]
|
||||
use windows::{
|
||||
core::*,
|
||||
Win32::{
|
||||
Storage::FileSystem::*,
|
||||
UI::Shell::*,
|
||||
UI::WindowsAndMessaging::*,
|
||||
System::Com::*,
|
||||
},
|
||||
Win32::{Storage::FileSystem::*, System::Com::*, UI::Shell::*, UI::WindowsAndMessaging::*},
|
||||
};
|
||||
|
||||
fn main() -> eframe::Result {
|
||||
@ -24,14 +19,14 @@ fn main() -> eframe::Result {
|
||||
unsafe {
|
||||
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
|
||||
}
|
||||
|
||||
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([1024.0, 768.0])
|
||||
.with_title("Nyash Explorer with Icons - アイコン付きドライブビューアー"),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
eframe::run_native(
|
||||
"Nyash Explorer Icons",
|
||||
options,
|
||||
@ -45,24 +40,25 @@ fn main() -> eframe::Result {
|
||||
// フォント設定
|
||||
fn setup_custom_fonts(ctx: &egui::Context) {
|
||||
let mut fonts = egui::FontDefinitions::default();
|
||||
|
||||
|
||||
fonts.font_data.insert(
|
||||
"noto_sans_jp".to_owned(),
|
||||
egui::FontData::from_static(include_bytes!("../assets/NotoSansJP-VariableFont_wght.ttf")).into(),
|
||||
egui::FontData::from_static(include_bytes!("../assets/NotoSansJP-VariableFont_wght.ttf"))
|
||||
.into(),
|
||||
);
|
||||
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(FontFamily::Proportional)
|
||||
.or_default()
|
||||
.insert(0, "noto_sans_jp".to_owned());
|
||||
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(FontFamily::Monospace)
|
||||
.or_default()
|
||||
.push("noto_sans_jp".to_owned());
|
||||
|
||||
|
||||
ctx.set_fonts(fonts);
|
||||
}
|
||||
|
||||
@ -93,13 +89,16 @@ impl NyashExplorer {
|
||||
explorer.refresh_drives();
|
||||
explorer
|
||||
}
|
||||
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_drive_icon(&self, drive_path: &str) -> Option<ColorImage> {
|
||||
unsafe {
|
||||
let mut shfi = SHFILEINFOW::default();
|
||||
let drive_path_wide: Vec<u16> = drive_path.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
|
||||
let drive_path_wide: Vec<u16> = drive_path
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
// アイコンを取得
|
||||
let result = SHGetFileInfoW(
|
||||
PCWSTR::from_raw(drive_path_wide.as_ptr()),
|
||||
@ -108,25 +107,25 @@ impl NyashExplorer {
|
||||
std::mem::size_of::<SHFILEINFOW>() as u32,
|
||||
SHGFI_ICON | SHGFI_LARGEICON | SHGFI_USEFILEATTRIBUTES,
|
||||
);
|
||||
|
||||
|
||||
if result == 0 || shfi.hIcon.is_invalid() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
// アイコンからビットマップを取得
|
||||
let icon_info = ICONINFO::default();
|
||||
if GetIconInfo(shfi.hIcon, &icon_info as *const _ as *mut _).is_ok() {
|
||||
// ビットマップからピクセルデータを取得する処理
|
||||
// アイコンを破棄
|
||||
let _ = DestroyIcon(shfi.hIcon);
|
||||
|
||||
|
||||
// C:ドライブの場合は保存済みBMPファイルを読み込む
|
||||
if drive_path.contains("C:") {
|
||||
if let Some(icon) = Self::load_bmp_icon("c_drive_icon.bmp") {
|
||||
return Some(icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// それ以外はダミーアイコンを返す
|
||||
Some(Self::create_dummy_icon(&drive_path))
|
||||
} else {
|
||||
@ -135,44 +134,46 @@ impl NyashExplorer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn get_drive_icon(&self, drive_path: &str) -> Option<ColorImage> {
|
||||
Some(Self::create_dummy_icon(drive_path))
|
||||
}
|
||||
|
||||
|
||||
// BMPファイルを読み込んでColorImageに変換
|
||||
fn load_bmp_icon(file_path: &str) -> Option<ColorImage> {
|
||||
let mut file = File::open(file_path).ok()?;
|
||||
let mut buffer = Vec::new();
|
||||
file.read_to_end(&mut buffer).ok()?;
|
||||
|
||||
|
||||
// BMPヘッダーをパース(簡易版)
|
||||
if buffer.len() < 54 {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
// BMPマジックナンバーをチェック
|
||||
if &buffer[0..2] != b"BM" {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
// ヘッダーから情報を読み取る
|
||||
let data_offset = u32::from_le_bytes([buffer[10], buffer[11], buffer[12], buffer[13]]) as usize;
|
||||
let data_offset =
|
||||
u32::from_le_bytes([buffer[10], buffer[11], buffer[12], buffer[13]]) as usize;
|
||||
let width = i32::from_le_bytes([buffer[18], buffer[19], buffer[20], buffer[21]]) as usize;
|
||||
let height = i32::from_le_bytes([buffer[22], buffer[23], buffer[24], buffer[25]]).abs() as usize;
|
||||
let height =
|
||||
i32::from_le_bytes([buffer[22], buffer[23], buffer[24], buffer[25]]).abs() as usize;
|
||||
let bits_per_pixel = u16::from_le_bytes([buffer[28], buffer[29]]);
|
||||
|
||||
|
||||
// 32ビットBMPのみサポート
|
||||
if bits_per_pixel != 32 {
|
||||
println!("Unsupported BMP format: {} bits per pixel", bits_per_pixel);
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
// ピクセルデータを読み取る
|
||||
let mut pixels = Vec::with_capacity(width * height);
|
||||
let pixel_data = &buffer[data_offset..];
|
||||
|
||||
|
||||
// BMPは下から上に格納されているので、反転しながら読み取る
|
||||
for y in (0..height).rev() {
|
||||
for x in 0..width {
|
||||
@ -188,18 +189,18 @@ impl NyashExplorer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some(ColorImage {
|
||||
size: [width, height],
|
||||
pixels,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// ダミーアイコンを生成(実際のアイコン取得が複雑なため)
|
||||
fn create_dummy_icon(drive_path: &str) -> ColorImage {
|
||||
let size = 48;
|
||||
let mut pixels = vec![egui::Color32::TRANSPARENT; size * size];
|
||||
|
||||
|
||||
// ドライブタイプに応じた色を設定
|
||||
let color = if drive_path.contains("C:") {
|
||||
egui::Color32::from_rgb(100, 149, 237) // コーンフラワーブルー
|
||||
@ -208,17 +209,17 @@ impl NyashExplorer {
|
||||
} else {
|
||||
egui::Color32::from_rgb(255, 182, 193) // ライトピンク
|
||||
};
|
||||
|
||||
|
||||
// シンプルな円形アイコンを描画
|
||||
let center = size as f32 / 2.0;
|
||||
let radius = (size as f32 / 2.0) - 4.0;
|
||||
|
||||
|
||||
for y in 0..size {
|
||||
for x in 0..size {
|
||||
let dx = x as f32 - center;
|
||||
let dy = y as f32 - center;
|
||||
let distance = (dx * dx + dy * dy).sqrt();
|
||||
|
||||
|
||||
if distance <= radius {
|
||||
pixels[y * size + x] = color;
|
||||
} else if distance <= radius + 2.0 {
|
||||
@ -227,13 +228,13 @@ impl NyashExplorer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ドライブ文字を中央に配置(簡易版)
|
||||
if let Some(_letter) = drive_path.chars().next() {
|
||||
// 文字の位置(中央)
|
||||
let text_x = size / 2 - 8;
|
||||
let text_y = size / 2 - 8;
|
||||
|
||||
|
||||
// 白い文字で描画
|
||||
for dy in 0..16 {
|
||||
for dx in 0..16 {
|
||||
@ -246,28 +247,28 @@ impl NyashExplorer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColorImage {
|
||||
size: [size, size],
|
||||
pixels,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn refresh_drives(&mut self) {
|
||||
self.drives.clear();
|
||||
self.status = "ドライブ情報を取得中...".to_string();
|
||||
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
unsafe {
|
||||
// 論理ドライブのビットマスクを取得
|
||||
let drives_mask = GetLogicalDrives();
|
||||
|
||||
|
||||
for i in 0..26 {
|
||||
if drives_mask & (1 << i) != 0 {
|
||||
let drive_letter = format!("{}:", (b'A' + i) as char);
|
||||
let drive_path = format!("{}\\", drive_letter);
|
||||
|
||||
|
||||
// ドライブ情報を取得
|
||||
let mut drive_info = DriveInfo {
|
||||
letter: drive_letter.clone(),
|
||||
@ -277,12 +278,15 @@ impl NyashExplorer {
|
||||
free_bytes: 0,
|
||||
icon_texture: None,
|
||||
};
|
||||
|
||||
|
||||
// ドライブタイプを取得
|
||||
let drive_type_code = GetDriveTypeW(PCWSTR::from_raw(
|
||||
format!("{}\0", drive_path).encode_utf16().collect::<Vec<u16>>().as_ptr()
|
||||
format!("{}\0", drive_path)
|
||||
.encode_utf16()
|
||||
.collect::<Vec<u16>>()
|
||||
.as_ptr(),
|
||||
));
|
||||
|
||||
|
||||
drive_info.drive_type = match drive_type_code {
|
||||
DRIVE_REMOVABLE => "リムーバブル".to_string(),
|
||||
DRIVE_FIXED => "ハードディスク".to_string(),
|
||||
@ -291,22 +295,29 @@ impl NyashExplorer {
|
||||
DRIVE_RAMDISK => "RAMディスク".to_string(),
|
||||
_ => "不明".to_string(),
|
||||
};
|
||||
|
||||
|
||||
// ボリューム情報を取得
|
||||
let mut volume_name = vec![0u16; 256];
|
||||
let mut file_system = vec![0u16; 256];
|
||||
let mut serial_number = 0u32;
|
||||
let mut max_component_len = 0u32;
|
||||
let mut file_system_flags = 0u32;
|
||||
|
||||
|
||||
if GetVolumeInformationW(
|
||||
PCWSTR::from_raw(format!("{}\0", drive_path).encode_utf16().collect::<Vec<u16>>().as_ptr()),
|
||||
PCWSTR::from_raw(
|
||||
format!("{}\0", drive_path)
|
||||
.encode_utf16()
|
||||
.collect::<Vec<u16>>()
|
||||
.as_ptr(),
|
||||
),
|
||||
Some(&mut volume_name),
|
||||
Some(&mut serial_number),
|
||||
Some(&mut max_component_len),
|
||||
Some(&mut file_system_flags),
|
||||
Some(&mut file_system),
|
||||
).is_ok() {
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let volume_name_str = String::from_utf16_lossy(&volume_name)
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
@ -318,38 +329,45 @@ impl NyashExplorer {
|
||||
} else {
|
||||
drive_info.name = format!("ドライブ ({})", drive_letter);
|
||||
}
|
||||
|
||||
|
||||
// 空き容量を取得
|
||||
let mut free_bytes_available = 0u64;
|
||||
let mut total_bytes = 0u64;
|
||||
let mut total_free_bytes = 0u64;
|
||||
|
||||
|
||||
if GetDiskFreeSpaceExW(
|
||||
PCWSTR::from_raw(format!("{}\0", drive_path).encode_utf16().collect::<Vec<u16>>().as_ptr()),
|
||||
PCWSTR::from_raw(
|
||||
format!("{}\0", drive_path)
|
||||
.encode_utf16()
|
||||
.collect::<Vec<u16>>()
|
||||
.as_ptr(),
|
||||
),
|
||||
Some(&mut free_bytes_available),
|
||||
Some(&mut total_bytes),
|
||||
Some(&mut total_free_bytes),
|
||||
).is_ok() {
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
drive_info.total_bytes = total_bytes;
|
||||
drive_info.free_bytes = total_free_bytes;
|
||||
}
|
||||
|
||||
|
||||
// アイコンを取得してテクスチャに変換
|
||||
if let Some(icon_image) = self.get_drive_icon(&drive_path) {
|
||||
let texture = self.ctx.load_texture(
|
||||
format!("drive_icon_{}", drive_letter),
|
||||
icon_image,
|
||||
Default::default()
|
||||
Default::default(),
|
||||
);
|
||||
drive_info.icon_texture = Some(texture);
|
||||
}
|
||||
|
||||
|
||||
self.drives.push(drive_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
// Windows以外の環境ではダミーデータ
|
||||
@ -361,32 +379,33 @@ impl NyashExplorer {
|
||||
free_bytes: 250_000_000_000,
|
||||
icon_texture: None,
|
||||
};
|
||||
|
||||
|
||||
if let Some(icon_image) = self.get_drive_icon("C:") {
|
||||
let texture = self.ctx.load_texture(
|
||||
"drive_icon_C:",
|
||||
icon_image,
|
||||
Default::default()
|
||||
);
|
||||
let texture =
|
||||
self.ctx
|
||||
.load_texture("drive_icon_C:", icon_image, Default::default());
|
||||
drive_info.icon_texture = Some(texture);
|
||||
}
|
||||
|
||||
|
||||
self.drives.push(drive_info);
|
||||
}
|
||||
|
||||
self.status = format!("{}個のドライブを検出しました(アイコン付き)", self.drives.len());
|
||||
|
||||
self.status = format!(
|
||||
"{}個のドライブを検出しました(アイコン付き)",
|
||||
self.drives.len()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
fn format_bytes(bytes: u64) -> String {
|
||||
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
|
||||
let mut size = bytes as f64;
|
||||
let mut unit_index = 0;
|
||||
|
||||
|
||||
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
|
||||
size /= 1024.0;
|
||||
unit_index += 1;
|
||||
}
|
||||
|
||||
|
||||
format!("{:.2} {}", size, UNITS[unit_index])
|
||||
}
|
||||
}
|
||||
@ -422,7 +441,7 @@ impl eframe::App for NyashExplorer {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// ツールバー
|
||||
egui::TopBottomPanel::top("toolbar").show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
@ -433,7 +452,7 @@ impl eframe::App for NyashExplorer {
|
||||
ui.label("Nyash Explorer - アイコン付きドライブビューアー");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// ステータスバー
|
||||
egui::TopBottomPanel::bottom("status_bar").show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
@ -443,27 +462,27 @@ impl eframe::App for NyashExplorer {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// メインパネル - ドライブ一覧
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("💾 ドライブ一覧(アイコン付き)");
|
||||
ui.separator();
|
||||
|
||||
|
||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||
for (index, drive) in self.drives.iter().enumerate() {
|
||||
let is_selected = self.selected_drive == Some(index);
|
||||
|
||||
|
||||
ui.group(|ui| {
|
||||
let response = ui.allocate_response(
|
||||
egui::vec2(ui.available_width(), 100.0),
|
||||
egui::Sense::click(),
|
||||
);
|
||||
|
||||
|
||||
if response.clicked() {
|
||||
self.selected_drive = Some(index);
|
||||
self.status = format!("{} を選択しました", drive.name);
|
||||
}
|
||||
|
||||
|
||||
// 背景色
|
||||
if is_selected {
|
||||
ui.painter().rect_filled(
|
||||
@ -472,13 +491,13 @@ impl eframe::App for NyashExplorer {
|
||||
egui::Color32::from_rgb(100, 149, 237).gamma_multiply(0.2),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
ui.allocate_new_ui(egui::UiBuilder::new().max_rect(response.rect), |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// ドライブアイコン
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(10.0);
|
||||
|
||||
|
||||
if let Some(texture) = &drive.icon_texture {
|
||||
ui.image((texture.id(), egui::vec2(48.0, 48.0)));
|
||||
} else {
|
||||
@ -493,19 +512,20 @@ impl eframe::App for NyashExplorer {
|
||||
ui.label(egui::RichText::new(icon_text).size(40.0));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ui.add_space(20.0);
|
||||
|
||||
|
||||
// ドライブ情報
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(10.0);
|
||||
ui.label(egui::RichText::new(&drive.name).size(16.0).strong());
|
||||
ui.label(format!("種類: {}", drive.drive_type));
|
||||
|
||||
|
||||
if drive.total_bytes > 0 {
|
||||
let used_bytes = drive.total_bytes - drive.free_bytes;
|
||||
let usage_percent = (used_bytes as f32 / drive.total_bytes as f32) * 100.0;
|
||||
|
||||
let usage_percent =
|
||||
(used_bytes as f32 / drive.total_bytes as f32) * 100.0;
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!(
|
||||
"使用領域: {} / {} ({:.1}%)",
|
||||
@ -514,7 +534,7 @@ impl eframe::App for NyashExplorer {
|
||||
usage_percent
|
||||
));
|
||||
});
|
||||
|
||||
|
||||
// 使用率バー
|
||||
let bar_width = 200.0;
|
||||
let bar_height = 10.0;
|
||||
@ -522,14 +542,14 @@ impl eframe::App for NyashExplorer {
|
||||
egui::vec2(bar_width, bar_height),
|
||||
egui::Sense::hover(),
|
||||
);
|
||||
|
||||
|
||||
// 背景
|
||||
ui.painter().rect_filled(
|
||||
rect,
|
||||
2.0,
|
||||
egui::Color32::from_gray(60),
|
||||
);
|
||||
|
||||
|
||||
// 使用領域
|
||||
let used_width = bar_width * (usage_percent / 100.0);
|
||||
let used_rect = egui::Rect::from_min_size(
|
||||
@ -549,18 +569,20 @@ impl eframe::App for NyashExplorer {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
ui.add_space(5.0);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// クイックアクション
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("🐱 Nyashについて").clicked() {
|
||||
self.status = "Nyash - Everything is Box! Windows APIでアイコンも取得できる化け物言語!".to_string();
|
||||
self.status =
|
||||
"Nyash - Everything is Box! Windows APIでアイコンも取得できる化け物言語!"
|
||||
.to_string();
|
||||
}
|
||||
|
||||
|
||||
if ui.button("📊 システム情報").clicked() {
|
||||
let total: u64 = self.drives.iter().map(|d| d.total_bytes).sum();
|
||||
let free: u64 = self.drives.iter().map(|d| d.free_bytes).sum();
|
||||
@ -573,4 +595,4 @@ impl eframe::App for NyashExplorer {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user