struct ExplorerUI { u8 search_input[64]; u8 secondary_input[64]; }; struct Explorer { // A bunch of flags? // Icon cache? // Array frame_textures; }; struct Ex1_NTFS_Enumeration { b32 initialized; b32 threads_started; ArrayView threads; Array threads_in_flight; }; // #Ex1_global_state global ExplorerUI explorer_ui; global Explorer explorer; global Ex1_NTFS_Enumeration ex1_ntfs; void ntfs_create_enumeration_threads (s32 thread_count) { if (!ex1_ntfs.initialized) { Timed_Block_Print("Thread initialization (ntfs)"); ex1_ntfs.initialized = true; ex1_ntfs.threads = ArrayView(thread_count); ex1_ntfs.threads_in_flight.allocator = GPAllocator(); for_each(t, ex1_ntfs.threads) { string thread_name = format_string("ntfs_enumeration_thread#%d", t); bool success = thread_init(&ex1_ntfs.threads[t], ntfs_enumeration_thread_proc, thread_name); Assert(success); } } } #define HOTKEY_ID_BRING_TO_FOREGROUND 1 #define VK_SPACE_KEY_CODE 0x20 // #define HOTKEY_ID_HIDE_TITLEBAR void Ex1_Unregister_Global_Hotkeys () { UnregisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND); } bool Ex1_Register_Global_Hotkeys () { if (!RegisterHotKey(nullptr, HOTKEY_ID_BRING_TO_FOREGROUND, MOD_CONTROL|MOD_SHIFT|MOD_ALT, VK_SPACE_KEY_CODE)) { Assert(false); log_error("Failed to register global hotkey Ctrl-Shift-Alt-Space"); return false; } return true; } #define USE_CTRL true #define USE_SHIFT true #define USE_ALT true #define TRIGGER_ONCE true #define TRIGGER_EVERY_FRAME false #define KEY_UNUSED false // #program_hotkeys (Not specific to Ex1) global Key_Combination program_exit_hotkey { ImGuiKey_W, USE_CTRL, USE_SHIFT, KEY_UNUSED, TRIGGER_ONCE }; global Key_Combination program_minimize_hotkey { ImGuiKey_W, USE_CTRL, KEY_UNUSED, KEY_UNUSED, TRIGGER_ONCE }; bool Ex1_check_key_combinations() { update_global_keyboard_state(); // #program_hotkeys if (check_key_combination(&program_exit_hotkey)) { return true; } if (check_key_combination(&program_minimize_hotkey)) { Win32_Minimize_Window_To_Tray(get_main_window_pointer()); } // #Ex1_hotkeys return false; } void Ex1_Control_Panel () { using namespace ImGui; Table* drive_table = get_drive_table(); Begin("Control Panel"); if (Button("Debug break")) { debug_break(); } if (Button("Discover drives") || !table_is_valid(drive_table)) { Win32_Discover_Drives(); } Text("drive_table is valid: %d", table_is_valid(drive_table)); push_allocator(temp()); ArrayView drives = os_get_available_drives(); // only includes drives that are ready. for_each(i, drives) { OS_Drive* drive = drives[i]; Text(" > [%d] drive letter: %s (is_present: %d)", drives.count + 1, drive->label.data, drive->is_present); if (drive->time_to_enumerate != 0) { SameLine(); Text("Enumerated in %.2f seconds", drive->time_to_enumerate); } // SameLine(); // if (Button(format_cstring("Read NTFS MFT Raw##%s", drive->label.data))) { // push_arena(thread_context()->arena); // Error* error = NTFS_MFT_read_raw(drive); // } } if (drives.count > 0 && Button("Enumerate all NTFS drives")) { // && ex1_ntfs.initialized // if drive count exceeds the number of threads, we need to group them so each thread // can enumerate multiple drives. // We need to distribute the drives across our available threads: push_allocator(GPAllocator()); Array> drive_split; drive_split.allocator = temp(); // this is only needed for this frame if (drives.count > os_cpu_physical_core_count()) { s32 thread_count = os_cpu_physical_core_count(); array_resize(drive_split, thread_count); ntfs_create_enumeration_threads(thread_count); s32 threads_to_create = thread_count; s64 drives_per_thread = (drives.count / thread_count); s64 remainder = drives.count % thread_count; s64 current_drive = 0; for_each(d, drive_split) { if (d == drive_split.count) { drive_split[d] = ArrayView(remainder); } else { drive_split[d] = ArrayView(drives_per_thread); } for (s64 i = 0; i < drive_split[d].count; i += 1) { drive_split[d][i] = drives[current_drive]; current_drive += 1; } } debug_break(); // #TODO: Check that the work has been distributed correctly. } else { // more threads than drives, or same amount s32 thread_count = (s32)drives.count; array_resize(drive_split, drives.count); ntfs_create_enumeration_threads(thread_count); for_each(d, drives) { auto drive = drives[d]; drive_split[d] = ArrayView(1); // Arrays of size one are sad :pensive: drive_split[d][0] = drive; } } s64 active_thread_count = drive_split.count; ex1_ntfs.threads_started = true; for (s64 t = 0; t < active_thread_count; t += 1) { Thread* thread = &ex1_ntfs.threads[t]; Arena* thread_arena = next_arena(Arena_Reserve::Size_64K); push_arena(thread_arena); auto thread_data = New(); thread_data->pool = thread_arena; thread_data->drives = drive_split[t]; thread_start(thread, thread_data); array_add(ex1_ntfs.threads_in_flight, thread); } } if (ex1_ntfs.threads_started && !ex1_ntfs.threads_in_flight.count) { // All threads are complete, we're free to clean up remaining memory push_allocator(GPAllocator()); array_free(ex1_ntfs.threads); array_free(ex1_ntfs.threads_in_flight); // Instead maybe we should just memset this to zero. reset_struct(&ex1_ntfs); } if (ex1_ntfs.threads_in_flight.count) { Text("Threads in flight: %d", ex1_ntfs.threads_in_flight.count); for_each(t, ex1_ntfs.threads_in_flight) { if (thread_is_done(ex1_ntfs.threads_in_flight[t])) { push_allocator(GPAllocator()); Thread* thread = ex1_ntfs.threads_in_flight[t]; auto task = thread_task(NTFS_Enumeration_Task); array_free(task->drives); // internal_free( // make sure to retreive any data you need to from here! release_arena(task->pool); thread_deinit(ex1_ntfs.threads_in_flight[t], false); array_unordered_remove_by_index(ex1_ntfs.threads_in_flight, t); t -= 1; // check this element index again! } } } End(); } void ImGui_Debug_Panel () { using namespace ImGui; push_allocator(temp()); Begin("Debug Panel"); SeparatorText("ex1_ntfs"); Text("Threads in flight count: %d", ex1_ntfs.threads_in_flight.count); for_each(i, ex1_ntfs.threads) { Text(" [%d] initialized: %d, has_context: %d, has_data: %d", i, ex1_ntfs.threads[i].proc != nullptr, ex1_ntfs.threads[i].context != nullptr, ex1_ntfs.threads[i].data != nullptr); } // #cpuid // Text("[cpus] physical: %d, logical: %d, primary: %d, secondary: %d", os_cpu_physical_core_count(), os_cpu_logical_core_count(), os_cpu_primary_core_count(), os_cpu_secondary_core_count()); SeparatorText("Arena In-Use List"); for (u8 i = 0; i < Arena_Reserve_Count; i += 1) { auto t = format_cstring( " [%s] in_use: %d, committed_bytes: %s", format_bytes(Arena_Sizes[i], 0).data, arena_free_list->in_flight_count[i], format_bytes(committed_bytes(arena_free_list->in_flight[i])).data ); Text(t); } SeparatorText("Arena Free List"); for (u8 i = 0; i < Arena_Reserve_Count; i += 1) { auto t = format_cstring( " [%s] free: %d, committed_bytes: %s", format_bytes(Arena_Sizes[i], 0).data, (s32)arena_free_list->free_table[i].count, format_bytes(committed_bytes(arena_free_list->free_table[i])).data ); Text(t); } SeparatorText("Child Threads"); SeparatorText("Errors"); ArrayView errors = get_all_errors(thread_context()); for_each(e, errors) { auto button_label = format_cstring("Clear##%d", e); if (Button(button_label)) { clear_error(thread_context(), errors[e]); continue; } SameLine(); Text(" [%d] %s", e, to_string(errors[e]).data); } Spacing(); if (Button("Push some error")) { log_warning("This is a warning."); log_error("... and this is an error."); } End(); }