From 3a22156ed39f83521d15771921cfd6224467ff91 Mon Sep 17 00:00:00 2001 From: Musa Mahmood Date: Thu, 4 Dec 2025 19:45:31 -0500 Subject: [PATCH] Add drive enumeration into hash table. --- exe_main.cpp | 14 +- extras/icons/tmp.ico | Bin 0 -> 153308 bytes extras/icons/tmp_min.ico | Bin 0 -> 15086 bytes lib/Base/Base.h | 1 + lib/Base/Hash_Table.h | 51 +++-- lib/Base/String.cpp | 25 ++- lib/Base/String.h | 2 + lib/OS/OS_Win32.cpp | 253 +++++++++++++++++++++- lib/OS/OS_Win32.h | 112 +++++++++- src/Base_Entry_Point.cpp | 26 +-- src/Ex1.cpp | 57 +++++ src/ImGui_Supplementary.cpp | 145 +++++++++++-- src/{imgui_main.cpp => explorer_main.cpp} | 61 ++++-- 13 files changed, 673 insertions(+), 74 deletions(-) create mode 100644 extras/icons/tmp.ico create mode 100644 extras/icons/tmp_min.ico create mode 100644 src/Ex1.cpp rename src/{imgui_main.cpp => explorer_main.cpp} (80%) diff --git a/exe_main.cpp b/exe_main.cpp index 8108303..ce5e9f0 100644 --- a/exe_main.cpp +++ b/exe_main.cpp @@ -1,14 +1,20 @@ #include "lib_main.cpp" -#define BASE_RUN_TESTS 0 +// Toggles: +#define BASE_RUN_TESTS 1 +#define BUILD_EXPLORER_APP_WIN32 1 +#define BUILD_CUSTOM_GUI 0 + #if BASE_RUN_TESTS #include "lib/Base/run_tests.cpp" #endif -#define USE_DEAR_IMGUI 1 -#if USE_DEAR_IMGUI +#if BUILD_EXPLORER_APP_WIN32 + static_assert(OS_WINDOWS && COMPILER_MSVC); #include #include + #include + #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "dxgi.lib") @@ -17,7 +23,7 @@ #include "lib/third_party/dear-imgui/imgui_impl_dx11.h" #include "src/ImGui_Supplementary.cpp" - #include "src/imgui_main.cpp" + #include "src/explorer_main.cpp" #endif #include "src/Base_Entry_Point.cpp" diff --git a/extras/icons/tmp.ico b/extras/icons/tmp.ico new file mode 100644 index 0000000000000000000000000000000000000000..236890e2f954724fdd11f2313c8e4794be512059 GIT binary patch literal 153308 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 z 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 literal 0 HcmV?d00001 diff --git a/extras/icons/tmp_min.ico b/extras/icons/tmp_min.ico new file mode 100644 index 0000000000000000000000000000000000000000..7ebd2edb1908cc9dc72f17adfadea813a65ba2ca GIT binary patch literal 15086 zcmeI3zi-q?6vrKL5-km)xZ={BbPYOau0}fPB&rm2yRZr!5;WB4q@dFlRQv~^6iECB z2qBarRjLyW5=s$WL6s`9mnGqnx$np8S)9SY#`dmbE+ai`&%7Vs_vX#>cs2yVN^l{# zbSY3<4etFA1ix{&I(vSp{%7jDb}f1SISBr^90b4U5s_8VO8jpS+z_AJj4>;1(qIr> z84SX|M1PCIgt%8F&;dMIIuhCA9J#cFlxyWu>o_q0r^+nJ4 zJLegVLi6g?p$Ws2n7F4e&4Y~fL-)jIJ`TUz=u7h;laeRS^KtCDjlMLG?!y-4o8oU6 zzc}Y=&mVDUl0P<%iH;&1x%q2#G+94ZhtD+#jA=sDnN`sZk*zZuajjBEs`+60Kl2WU z(T}3vwLZt9H=(6y;=+=XG+eDo*K+*0N&2npNCcV}-gNJc!Km=bz2)?!RU- zIf;pT`WP#d$3|9tpl)caaDCsuADZ{?znG(=Z}$=OF$PZwhJ9?gF^~*(Ems!b&|lWz zsSt;8hJEU1u6^R0<+qCm9(ap%Y3y}|eQJ2F9CKW?!CRzDW4jsld5`4Ek!w{OyhXY+ zc5nvmcOCy_=HHu)_;d0eKdgOwr9LDziaPmP#QTDGCd55`t$%-6``_y|brjIaE$0**k*t<|OEd)d5h*ICo@U3JEc*CC-e60k@@)X*i5Gy~HV|D!l6WGdp5YN*( zHXc6Y9J~B17an-)*Z^Av{CmE-A3vU$D5`!A3@HDI3&1XF}ZN6(7j0O0&eDG6Knl^O! z*cAS)Jp9tbH(zCa)WxO^9X>X}Sb+c3-zAmlrM@?9=qR(}jL-iL%JoBheaGMH{9$c4 zb5%IEXKnrVy?*xL{RKvM-P-5mTemQ0-27Vh{-NxfSUI%kVk~<;EBiO*LHPT=fA017 z3v?St*kuKd1Upy&?0%k;`9BUtb^X3pMbyb=U4d9=kr7GLw!EKP5YoV)XVd8 z>jSx@8q>?)FRTx{IPxzgs b bool table_is_valid (Table* table) { if (table == nullptr) return false; - if (table->entries == nullptr) return false; - if (table->entries->allocated == 0) return false; + if (table->entries.data == nullptr) return false; + if (table->allocated == 0) return false; if (table->hash_function == nullptr) return false; if (table->compare_function == nullptr) return false; @@ -49,8 +49,8 @@ template bool table_is_valid (Table* table) { return true; } -template void table_init (Table* table, s64 slots_to_allocate=64, Arena_Reserve new_reserve=Arena_Reserve::Size_64M) { - if (table->allocator->proc == nullptr) { +template void table_init (Table* table, s64 slots_to_allocate=64) { + if (table->allocator.proc == nullptr) { table->allocator = context_allocator(); // #remember_allocator } push_allocator(table->allocator); @@ -64,8 +64,8 @@ template void table_init (Table* table, s64 slots table->entries[i].hash = 0; } // default hash and compare functions: - table->hash_function = table_hash_function_fnv1a; - table->compare_function = u64_keys_match; + // table->hash_function = table_hash_function_fnv1a; + // table->compare_function = u64_keys_match; } // Adds given key value pair to the table, returns a pointer to the inserted value. @@ -73,13 +73,13 @@ template U* table_add (Table* table, T key, U val Assert(table_is_valid(table)); Assert(table->load_factor_percent < 100); - if ( ((table->slots_filled + 1) * 100) >= (table->entries->allocated * table->load_factor_percent) ) { - table_resize(table, Next_Power_Of_Two(table->entries->allocated + 64)); + if ( ((table->slots_filled + 1) * 100) >= (table->allocated * table->load_factor_percent) ) { + table_resize(table, Next_Power_Of_Two(table->allocated + 64)); } - Assert(table->slots_filled < table->entries->allocated); + Assert(table->slots_filled < table->allocated); // #Walk_Table - u32 mask = (u32)(table->entries->allocated - 1); + u32 mask = (u32)(table->allocated - 1); u32 hash = table->hash_function(&key, sizeof(T)); @@ -126,7 +126,7 @@ template U* table_find_pointer (Table* table, T k if (!table_is_valid(table)) return nullptr; // #Walk_Table - u32 mask = (u32)(table->entries->allocated - 1); + u32 mask = (u32)(table->allocated - 1); u32 hash = table->hash_function(&key, sizeof(T)); @@ -142,7 +142,7 @@ template U* table_find_pointer (Table* table, T k Table_Entry* entry = &table->entries[index]; if (entry->hash == hash) { if (table->compare_function(&entry->key, &key)) { - return &entry.value + return &entry->value; } } @@ -154,7 +154,7 @@ template U* table_find_pointer (Table* table, T k table_while_loop = table->entries[index].hash; } - return nullptr + return nullptr; } template U* table_set (Table* table, T key, U value) { @@ -168,7 +168,7 @@ template U* table_set (Table* table, T key, U val } template void table_resize (Table* table, s64 slots_to_allocate) { - s64 initial_count = table->entries->count; + s64 initial_count = table->entries.count; Assert(slots_to_allocate > initial_count); if (slots_to_allocate <= initial_count) { return; @@ -200,7 +200,7 @@ template void table_reset (Table* table, bool kee table->count = 0; table->slots_filled = 0; - for (s64 i = 0; i < table->entries->count; i += 1) { + for (s64 i = 0; i < table->entries.count; i += 1) { table->entries[i].hash = 0; } @@ -235,7 +235,7 @@ template bool table_remove (Table* table, T key, if (!table_is_valid(table)) return nullptr; // #Walk_Table - u32 mask = (u32)(table->entries->allocated - 1); + u32 mask = (u32)(table->allocated - 1); u32 hash = table->hash_function(&key, sizeof(T)); @@ -268,12 +268,12 @@ template bool table_remove (Table* table, T key, // #TODO: we need a for expansion iterator? // table_find_multiple (put results in Temp-backed Array<>, and return it as an ArrayView) { -template ArrayView table_remove (Table* table, T key, U* value) { +template ArrayView table_find_multiple (Table* table, T key, U* value) { Array results; results.allocator = temp(); // #Walk_Table - u32 mask = (u32)(table->entries->allocated - 1); + u32 mask = (u32)(table->allocated - 1); u32 hash = table->hash_function(&key, sizeof(T)); @@ -307,9 +307,24 @@ template ArrayView table_remove (Table* table, return to_view(results); } + + // #TODO: // find_or_add is kind of like table_set, but used when you // just want a pointer to the value, which you can fill in. +template U* table_find_or_add (Table* table, T key, bool* newly_added) { + U* value = table_find_pointer(table, key); + if (value) { + (*newly_added) = false; + return value; + } + + U new_value; + value = table_add(table, key, new_value); + (*newly_added) = true; + return value; +} + // find_or_add :: (table: *Table, key: table.Key_Type) -> (entry: *table.Value_Type, newly_added: bool) { // value := table_find_pointer(table, key); // if value return value, false; diff --git a/lib/Base/String.cpp b/lib/Base/String.cpp index ee589dc..04e3189 100644 --- a/lib/Base/String.cpp +++ b/lib/Base/String.cpp @@ -97,7 +97,7 @@ string wide_to_utf8 (u16* source, s32 length) { u8* memory = NewArray(query_result); string utf8_string; - utf8_string.count = query_result; + utf8_string.count = query_result - 1; // null terminator is not counted utf8_string.data = memory; s32 result = WideCharToMultiByte(CP_UTF8, 0, (LPCWCH)source, length, @@ -259,3 +259,26 @@ force_inline string builder_to_string (String_Builder* sb) { internal force_inline void free_string_builder (String_Builder* sb) { arena_array_free(*sb); } + +char to_lower_ascii(char c) { + if (c >= 'A' && c <= 'Z') + c = c + ('a' - 'A'); // or c += 32; + return c; +} + +char to_upper_ascii(char c) { + if (c >= 'a' && c <= 'z') + c = c - ('a' - 'A'); // or c -= 32; + return c; +} + +// string to_lower_in_place (string s) { } +// Input must be ascii or utf8! +string to_lower_copy (string s_orig) { + string s = copy_string(s_orig); + for (s64 i = 0; i < s.count; i += 1) { + s.data[i] = to_lower_ascii(s.data[i]); + } + + return s; +} \ No newline at end of file diff --git a/lib/Base/String.h b/lib/Base/String.h index 1326ee4..39e467a 100644 --- a/lib/Base/String.h +++ b/lib/Base/String.h @@ -90,6 +90,8 @@ wstring utf8_to_wide (string source); string format_string (char* format, ...); string format_string_no_context (char* format, ...); +string to_lower_copy (string s_orig); + // #TODO #Parsing stuff: // is_white_space(char: u8) // advance diff --git a/lib/OS/OS_Win32.cpp b/lib/OS/OS_Win32.cpp index 436527b..a1f8af3 100644 --- a/lib/OS/OS_Win32.cpp +++ b/lib/OS/OS_Win32.cpp @@ -35,9 +35,12 @@ struct OS_System_Info { u64 allocation_granularity; string machine_name; - // Monitor stuff: + // #Monitors b32 monitors_enumerated; Array monitors; // Back with GPAllocator + + // #Drives + Table drives; }; struct OS_Process_Info { @@ -62,7 +65,7 @@ struct OS_State_Win32 { }; global OS_State_Win32 global_win32_state; -internal b32 global_win32_is_quiet = 0; // No console output +internal b32 global_win32_is_quiet = 0; // No console output (`quiet` flag passed) internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs) { if (global_win32_is_quiet) { ExitProcess(1); } @@ -335,6 +338,11 @@ internal string get_error_string (OS_Error_Code error_code) { return result; // trim_right(result, "\r\n"); } +internal void log_error_code_and_string () { + OS_Error_Code error_code = GetLastError(); + log_error(" > GetLastError code: %d, %s\n", error_code, get_error_string(error_code).data); +} + internal bool file_is_valid (File file) { if (file.handle == INVALID_HANDLE_VALUE) return false; if (file.handle == 0) return false; @@ -625,6 +633,129 @@ Window_Dimensions platform_get_centered_window_dimensions (bool open_on_largest_ return dimensions; } +bool Win32_Set_Main_Icon () { + Window_Info* info = get_main_window_pointer(); + if (info->icon) { + HICON old_icon = (HICON)SendMessage(info->window, WM_SETICON, ICON_BIG, (LPARAM)info->icon); + if (old_icon) DestroyIcon(old_icon); + return true; + } + + return false; +} + +#define ICON_CONTEXT_MENU_ITEM_ID 5011 +#define MAIN_WINDOW_TRAY_ICON_ID 5001 +#define WM_TRAYICON WM_USER + 1 // our own value to identify when receiving a message. + +bool Win32_Set_Tray_Icon (string tooltip_text) { + Window_Info* info = get_main_window_pointer(); + if (info->icon_minimized) { + push_allocator(temp()); + wstring tooltip_text_wide = utf8_to_wide(tooltip_text); + Assert(tooltip_text_wide.count < 128); + + info->nid.cbSize = sizeof(NOTIFYICONDATAW); + info->nid.hWnd = info->window; + info->nid.uID = MAIN_WINDOW_TRAY_ICON_ID; + info->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + info->nid.uCallbackMessage = WM_TRAYICON; // Custom message when interacting with the tray icon + info->nid.hIcon = info->icon_minimized; + memcpy(info->nid.szTip, tooltip_text_wide.data, tooltip_text_wide.count); + + bool success = Shell_NotifyIconW(NIM_ADD, &info->nid); + if (success) { + info->tray_icon_added = true; + return true; + } + } + + return false; +} + +bool Win32_Show_Tray_Icon () { + Window_Info* info = get_main_window_pointer(); + if (info && info->tray_icon_added) { + return Shell_NotifyIconW(NIM_ADD, &info->nid); + } + return false; +} + +bool Win32_Hide_Tray_Icon () { + Window_Info* info = get_main_window_pointer(); + if (info && info->tray_icon_added) { + return Shell_NotifyIconW(NIM_DELETE, &info->nid); + } + return false; +} + +bool Win32_Hide_Window_Titlebar () { + Window_Info* info = get_main_window_pointer(); + if (info && info->window) { + LONG style = GetWindowLongW(info->window, GWL_STYLE); + style &= (LONG)(~(WS_CAPTION | WS_SYSMENU)); + SetWindowLongW(info->window, GWL_STYLE, style); + SetWindowPos(info->window, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); + return true; + } + return false; +} + +bool Win32_Show_Window_Titlebar () { + Window_Info* info = get_main_window_pointer(); + if (info && info->window) { + LONG style = GetWindowLongW(info->window, GWL_STYLE); + style |= (LONG)(WS_CAPTION | WS_SYSMENU); + SetWindowLongW(info->window, GWL_STYLE, style); + SetWindowPos(info->window, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); + return true; + } + return false; +} + +void Win32_Minimize_Window_To_Tray (Window_Info* info) { + if (info && info->window) { + ShowWindow(info->window, SW_HIDE); + Win32_Show_Tray_Icon(); + info->minimized_to_tray = true; + } +} + +void Win32_Restore_Window_From_Tray (Window_Info* info) { + if (info && info->window && info->minimized_to_tray) { + ShowWindow(info->window, SW_RESTORE); + Win32_Hide_Tray_Icon(); + info->minimized_to_tray = false; + } +} + +// Win32_Restore_Window_From_Tray(); +void Win32_Bring_Window_To_Foreground (Window_Info* info) { + Win32_Restore_Window_From_Tray(info); + if (info && info->window) { + if (os_window_is_minimized(info->window)) { + ShowWindow(info->window, SW_RESTORE); + } + SetForegroundWindow(info->window); + } +} + +bool Win32_Load_Main_Window_Icon_Minimized (string icon_path) { + HICON result = (HICON)LoadImageW(nullptr, (LPCWSTR)utf8_to_wide(icon_path).data, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + Window_Info* info = get_main_window_pointer(); + info->icon_minimized = result; + + return (result != nullptr); +} + +bool Win32_Load_Main_Window_Icon (string icon_path) { + HICON result = (HICON)LoadImageW(nullptr, (LPCWSTR)utf8_to_wide(icon_path).data, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + Window_Info* info = get_main_window_pointer(); + info->icon = result; + + return (result != nullptr); +} + // #window_creation -> put API in OS_Win32.h // Instead of returning WindowType, return the handle + other information. bool os_create_window (string new_window_name, Window_Type parent, bool center_window, bool open_on_largest_monitor, bool display_window, void* wnd_proc_override) { @@ -644,7 +775,7 @@ bool os_create_window (string new_window_name, Window_Type parent, bool center_w wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = nullptr; - wc.hIcon = (HICON)LoadImageW(nullptr, (LPCWSTR)utf8_to_wide("tmp.ico").data, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + wc.hIcon = get_main_window().icon; wc.hCursor = LoadCursorW(nullptr, (LPCWSTR)IDC_ARROW); wc.hbrBackground = nullptr; wc.lpszMenuName = nullptr; @@ -700,7 +831,9 @@ bool os_create_window (string new_window_name, Window_Type parent, bool center_w Window_Info get_main_window () { Array windows = global_win32_state.process_info.windows; - Assert(windows.count > 0); + if (windows.count <= 0) { + return {}; + } for (s64 i = 0; i < windows.count; i += 1) { if (windows[i].is_main_window) { @@ -723,6 +856,18 @@ Window_Info* get_main_window_pointer () { return nullptr; } +Window_Info* get_window_info (Window_Type window) { + s64 window_count = global_win32_state.process_info.windows.count; + + for (s64 i = 0; i < window_count; i += 1) { + if (global_win32_state.process_info.windows[i].window == window) { + return &global_win32_state.process_info.windows[i]; + } + } + + return nullptr; +} + bool get_window_dimensions(Window_Info* info, s32* width, s32* height) { if (info == nullptr || width == nullptr || height == nullptr) return false; Assert(width && height); @@ -742,6 +887,106 @@ bool get_window_dimensions(Window_Info* info, s32* width, s32* height) { return true; } +bool os_window_is_minimized (Window_Type window) { + return (bool)IsIconic(window); +} + +bool os_main_window_is_minimized () { + return os_window_is_minimized(get_main_window().window); +} + +// #Drives +constexpr u64 Win32_Max_Path_Length = 260; +bool Win32_Discover_Drives () { + push_allocator(GPAllocator()); + // Initialize drive_table if necessary. + Table* drive_table = &global_win32_state.system_info.drives; + if (!drive_table->allocated) { + drive_table->allocator = GPAllocator(); + // #TODO: #hash_table need a macro for string keys! + drive_table->hash_function = string_hash_function_fnv1a; + drive_table->compare_function = string_keys_match; + s64 slots_to_allocate = 64; + table_init(drive_table, slots_to_allocate); + } + + u16 lpBuf[1024]; + u32 result_length = GetLogicalDriveStringsW(1024, (LPWSTR)lpBuf); + if (!result_length) { + log_error("GetLogicalDriveStringsW failed!"); + log_error_code_and_string(); + return false; + } + + bool completed = false; + s32 current_index = 0; + + while (true) { + completed = completed || (current_index >= 1024) || lpBuf[current_index] == 0; + if (completed) break; + + u16* logical_drive = lpBuf + current_index; + + string drive_label = wide_to_utf8(logical_drive); + + u64 total_number_of_bytes;u64 total_number_of_free_bytes; + bool result = GetDiskFreeSpaceExW((LPCWSTR)logical_drive, nullptr, + (PULARGE_INTEGER)&total_number_of_bytes, (PULARGE_INTEGER)&total_number_of_free_bytes); + + Win32_Drive_Type drive_type = (Win32_Drive_Type)GetDriveTypeW((LPCWSTR)logical_drive); + + log("Found %s drive, type %s", drive_label.data, to_string(drive_type).data); + + current_index += (s32)(drive_label.count + 1); + + bool just_added = false; + Win32_Drive* drive = table_find_or_add(drive_table, drive_label, &just_added); + + if (!just_added) { // delete old strings before updating + // This is silly, but there's a small chance the volume has been renamed so... + string_free(drive->label); // this is actually just stupid. + string_free(drive->volume_name); + } + + u16 volume_name[Win32_Max_Path_Length] = {}; + u16 file_system_name[Win32_Max_Path_Length] = {}; + DWORD serial_number = 0; DWORD max_comp_len = 0; DWORD file_system_flags = 0; + if (GetVolumeInformationW((LPCWSTR)logical_drive, (LPWSTR)volume_name, + Win32_Max_Path_Length, &serial_number, &max_comp_len, &file_system_flags, + (LPWSTR)file_system_name, Win32_Max_Path_Length)) { + drive->label = drive_label; + drive->volume_name = wide_to_utf8(volume_name); + if (drive->volume_name == "") { drive->volume_name = copy_string("Local Disk"); } + drive->type = (Win32_Drive_Type)drive_type; + { push_allocator(temp()); + drive->file_system = Win32_filesystem_from_string(wide_to_utf8(file_system_name)); + } + drive->serial_number = serial_number; + drive->max_component_length = max_comp_len; + drive->file_system_flags = file_system_flags; + drive->is_present = true; + push_allocator(temp()); + log(" - volume name: %s", drive->volume_name.data); + log(" - file_system: %s", wide_to_utf8(file_system_name).data); + } else { + log_error("GetVolumeInformationW failed!"); + log_error_code_and_string(); + drive->is_present = false; + } + } + + return true; +} + +bool Win32_Drive_Exists (string drive_letter) { + push_allocator(temp()); + LPCWSTR drive_letter_wide = (LPCWSTR)utf8_to_wide(drive_letter).data; + UINT type = GetDriveTypeW(drive_letter_wide); + return (type != DRIVE_UNKNOWN && type != DRIVE_NO_ROOT_DIR); + // Alternative method: + // return (bool)GetVolumeInformationW(drive_letter_wide, nullptr, 0, nullptr, nullptr, nullptr, nullptr, 0); +} + // #TODO: #window_creation // [ ] resize_window // [ ] position_window diff --git a/lib/OS/OS_Win32.h b/lib/OS/OS_Win32.h index 1e71dca..ef729dd 100644 --- a/lib/OS/OS_Win32.h +++ b/lib/OS/OS_Win32.h @@ -1,4 +1,6 @@ #pragma comment(lib, "user32") +#pragma comment(lib, "shell32") +#include f64 GetUnixTimestamp (); s64 GetUnixTimestampNanoseconds (); @@ -47,6 +49,7 @@ internal bool write_entire_file (string file_path, ArrayView file_data); // #window_creation typedef HWND Window_Type; +typedef HICON Window_Icon; struct Window_Dimensions { s32 window_x; @@ -58,12 +61,17 @@ struct Window_Dimensions { struct Window_Info { Window_Type window; Window_Dimensions initial_dimensions; // for resetting. + Window_Icon icon; + Window_Icon icon_minimized; - - b32 is_main_window; - b32 backend_initialized; + bool is_main_window; + bool tray_icon_added; + bool minimized_to_tray; // Platform-Specific (Win32) + HMENU tray_icon_menu; HDC hdc; + // Likely will only be used for main window: + NOTIFYICONDATAW nid; }; // #move: Monitor - platform-independent hardware monitor information: @@ -79,6 +87,104 @@ struct Monitor { #endif }; +enum class Win32_Drive_Type: s32 { + Unknown = 0, + No_Root_Dir = 1, + Removable = 2, + Fixed = 3, + Remote = 4, + cdrom = 5, + ramdisk = 6 +}; + +enum class File_System: s32 { + Unknown = 0, + MusaFS = 1, + exFAT = 2, + Ext2 = 65, + Ext3 = 66, + Ext4 = 67, + Btrfs = 79, + XFS = 86, + ZFS = 91, + NTFS = 128, // Windows + ReFS = 130, // Windows + AFS = 256, // Apple File System + F2FS = 1024, + // Legacy systems: + FAT32 = -1, + // FAT16 :: -2; + // FAT12 :: -3; +}; + +string to_string (Win32_Drive_Type type) { + switch (type) { + case Win32_Drive_Type::Unknown: { return "Unknown"; } break; + case Win32_Drive_Type::No_Root_Dir: { return "No_Root_Dir"; } break; + case Win32_Drive_Type::Removable: { return "Removable"; } break; + case Win32_Drive_Type::Fixed: { return "Fixed"; } break; + case Win32_Drive_Type::Remote: { return "Remote"; } break; + case Win32_Drive_Type::cdrom: { return "cdrom"; } break; + case Win32_Drive_Type::ramdisk: { return "ramdisk"; } break; + } + + Assert(false); + return "Unknown"; +} + +string to_string (File_System fs) { + switch (fs) { + case File_System::Unknown: { return "Unknown"; } break; + case File_System::MusaFS: { return "MusaFS"; } break; + case File_System::exFAT: { return "exFAT"; } break; + case File_System::Ext2: { return "Ext2"; } break; + case File_System::Ext3: { return "Ext3"; } break; + case File_System::Ext4: { return "Ext4"; } break; + case File_System::Btrfs: { return "Btrfs"; } break; + case File_System::XFS: { return "XFS"; } break; + case File_System::ZFS: { return "ZFS"; } break; + case File_System::NTFS: { return "NTFS"; } break; + case File_System::ReFS: { return "ReFS"; } break; + case File_System::AFS: { return "AFS"; } break; + case File_System::F2FS: { return "F2FS"; } break; + case File_System::FAT32: { return "FAT32"; } break; + } + + Assert(false); + return "Unknown"; +} + +File_System Win32_filesystem_from_string (string s) { + string s_copy = to_lower_copy(s); + + if (s_copy == "ntfs") { return File_System::NTFS; } + if (s_copy == "refs") { return File_System::ReFS; } + if (s_copy == "fat") { return File_System::FAT32; } + if (s_copy == "exfat") { return File_System::exFAT; } + + Assert(false); + return File_System::Unknown; +} + +struct Win32_Drive { + string label; + string volume_name; + Win32_Drive_Type type; + File_System file_system; + s64 full_size; + s64 free_space; + u32 serial_number; + u32 max_component_length; + u32 file_system_flags; + bool is_present; + // Not sure if this should be here... + // f32 enumeration_time; + // f64 last_seen_alive_timestamp; +}; + +bool os_window_is_minimized (Window_Type window); + +bool os_main_window_is_minimized (); bool os_create_window (string new_window_name, Window_Type parent=nullptr, bool center_window=true, bool open_on_largest_monitor=true, bool display_window=true, void* wnd_proc_override=nullptr); diff --git a/src/Base_Entry_Point.cpp b/src/Base_Entry_Point.cpp index d42489c..02bccc6 100644 --- a/src/Base_Entry_Point.cpp +++ b/src/Base_Entry_Point.cpp @@ -7,7 +7,7 @@ internal void Main_Entry_Point (int argc, WCHAR **argv); return 0; } #else - #include // globals __argc, __argv + #include // globals __argc, __wargv int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { Main_Entry_Point(__argc, __wargv); @@ -16,7 +16,7 @@ internal void Main_Entry_Point (int argc, WCHAR **argv); #endif #endif -internal void Main_Entry_Point (int argc, WCHAR **argv) { +internal void Main_Entry_Point (int argc, WCHAR **argv) { // #entry_point set_cpu_base_frequency(3200); // REQUIRED FOR TIMING MODULE! will depend on CPU #if BASE_RUN_TESTS @@ -25,11 +25,9 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { // #NOTE: Be careful using a timing or auto-release macros // before setting up the thread context! Bootstrap_Main_Thread_Context(); + #if OS_WINDOWS Win32_Entry_Point(argc, argv); - Win32_Initialize_Ex(); - // #TODO: Initialize global hotkeys - ImGui_Application_Win32(); #endif #if OS_LINUX // Linux_Entry_Point(argc, argv); @@ -37,22 +35,20 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { #if BASE_RUN_TESTS run_post_setup_tests(); #endif +#if BUILD_EXPLORER_APP_WIN32 // #entry_point + Explorer_ImGui_Application_Win32(); // #rename +#endif +#if BUILD_CUSTOM_GUI + // Custom GUI based on Vik's prototype [WIP] /* Alternative if we're using OpenGL. - s32 window_width; s32 window_height; - success = get_window_dimensions(&main_window, &window_width, &window_height); - Assert(success); - // Maybe bake screen into window_info? - // Rect main_screen = make_rect_int(0, 0, window_width, window_height); - graphics_set_render_target(main_window); - - // get_render_dimensions + graphics_set_render_target(get_main_window()); // 3. Init Graphics (DX11 or OpenGL3) - // Maybe start with OpenGL3 // #TODO: #Main - `Main_Entry_Point` // 4. [ ] Setup Mouse and Keyboard Inputs // 5. [ ] Launch second thread; thread groups - */ + */ +#endif } diff --git a/src/Ex1.cpp b/src/Ex1.cpp new file mode 100644 index 0000000..8ecd759 --- /dev/null +++ b/src/Ex1.cpp @@ -0,0 +1,57 @@ +struct ExplorerUI { + u8 search_input[64]; + u8 secondary_input[64]; + +}; + +struct Explorer { + // A bunch of flags? + + // Icon cache? + // Array frame_textures; +}; + +// #Ex1_global_state +global ExplorerUI explorer_ui; +global Explorer explorer; + +#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-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 + +// #global_hotkeys - should rename to 'generic program hotkeys' +global Key_Combination global_exit_hotkey { ImGuiKey_W, USE_CTRL, USE_SHIFT, KEY_UNUSED, TRIGGER_ONCE }; +global Key_Combination global_minimize_hotkey { ImGuiKey_W, USE_CTRL, KEY_UNUSED, KEY_UNUSED, TRIGGER_ONCE }; + +bool Ex1_check_key_combinations() { + update_global_keyboard_state(); + // #global_hotkeys + if (check_key_combination(&global_exit_hotkey)) { + return true; + } + if (check_key_combination(&global_minimize_hotkey)) { + Win32_Minimize_Window_To_Tray(get_main_window_pointer()); + } + // #Ex1_hotkeys + return false; +} \ No newline at end of file diff --git a/src/ImGui_Supplementary.cpp b/src/ImGui_Supplementary.cpp index 4eea72a..5220c33 100644 --- a/src/ImGui_Supplementary.cpp +++ b/src/ImGui_Supplementary.cpp @@ -87,26 +87,124 @@ LRESULT WINAPI ImGui_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) return true; switch (msg) { - case WM_SIZE: - if (wParam == SIZE_MINIMIZED) + case WM_SIZE: { + if (wParam == SIZE_MINIMIZED) + return 0; + g_ResizeWidth = (UINT)LOWORD(lParam); // Queue resize + g_ResizeHeight = (UINT)HIWORD(lParam); + + } return 0; + case WM_SYSCOMMAND: { + if ((wParam & 0xfff0) == SC_CLOSE || (wParam & 0xfff0) == SC_MINIMIZE) { + Win32_Minimize_Window_To_Tray(get_window_info(hWnd)); return 0; - g_ResizeWidth = (UINT)LOWORD(lParam); // Queue resize - g_ResizeHeight = (UINT)HIWORD(lParam); - - return 0; - case WM_SYSCOMMAND: - if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu - return 0; - break; - case WM_DESTROY: - ::PostQuitMessage(0); - return 0; - } + } + if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu + return 0; + } break; + case WM_CREATE: { + // This is pretty dumb to have a single one for global state! See if we can attach it to Window_Info + } return 0; + case WM_TRAYICON: { + // Most applications perform actions on button up (after click), but we do + // on button down to make it feel as responsive as possible. + if (lParam == WM_LBUTTONDOWN) { + Win32_Bring_Window_To_Foreground(get_main_window_pointer()); + } + if (lParam == WM_RBUTTONDOWN) { + POINT curPos; GetCursorPos(&curPos); + SetForegroundWindow(hWnd); + Window_Info* info = get_window_info(hWnd); + if (!info) return 0; + u32 clicked = TrackPopupMenu(info->tray_icon_menu, TPM_RETURNCMD | TPM_NONOTIFY, curPos.x, curPos.y, 0, hWnd, nullptr); + if (clicked == ICON_CONTEXT_MENU_ITEM_ID) { + log("(WM_TRAYICON:Exit) Quitting application..."); + ::PostQuitMessage(0); + } + } + } return 0; + case WM_DESTROY: { + ::PostQuitMessage(0); + } return 0; + } // case (msg) return ::DefWindowProcW(hWnd, msg, wParam, lParam); } +// ~ Keyboard Input +struct Key_Combination { + ImGuiKey key = ImGuiKey_None; + bool ctrl; + bool shift; + bool alt; + bool once = true; + + bool triggered; + + f64 last_key_down_time; +}; +struct KB_State { + f64 current_frame_time; + b32 ctrl_key_down; + b32 shift_key_down; + b32 alt_key_down; +}; + +global KB_State global_keyboard_state; + +force_inline bool key_combination_failed(Key_Combination* kc) { + return false; +} + +bool is_ctrl_key_down () { return (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)); } +bool is_shift_key_down () { return (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)); } +bool is_alt_key_down () { return (ImGui::IsKeyDown(ImGuiKey_LeftAlt) || ImGui::IsKeyDown(ImGuiKey_RightAlt)); } + +bool check_key_combination (Key_Combination* kc) { + kc->triggered = false; // reset + + if (kc->last_key_down_time && ImGui::IsKeyReleased(kc->key)) { + kc->last_key_down_time = 0.0; + } + + if ( kc->ctrl && !is_ctrl_key_down()) return key_combination_failed(kc); + if (!kc->ctrl && is_ctrl_key_down()) return key_combination_failed(kc); + if ( kc->shift && !is_shift_key_down()) return key_combination_failed(kc); + if (!kc->shift && is_shift_key_down()) return key_combination_failed(kc); + if ( kc->alt && !is_alt_key_down()) return key_combination_failed(kc); + if (!kc->alt && is_alt_key_down()) return key_combination_failed(kc); + + if (kc->once) { + kc->triggered = (ImGui::IsKeyDown(kc->key) && kc->last_key_down_time == 0); + if (kc->triggered) { + kc->last_key_down_time = global_keyboard_state.current_frame_time; + } + } else { + kc->triggered = (ImGui::IsKeyDown(kc->key)); + } + + if (kc->triggered) { +#if BUILD_DEBUG + log("Key triggered: %s%s%s%s | once=%d, ts: %1.3f", + (kc->ctrl) ? "ctrl+" : "", + (kc->shift) ? "shift+" : "", + (kc->alt) ? "alt+" : "", + ImGui::GetKeyName(kc->key), kc->once, kc->last_key_down_time); +#endif + } + + return kc->triggered; +} + + +void update_global_keyboard_state () { + global_keyboard_state.current_frame_time = GetUnixTimestamp(); + global_keyboard_state.ctrl_key_down = is_ctrl_key_down(); + global_keyboard_state.shift_key_down = is_shift_key_down(); + global_keyboard_state.alt_key_down = is_alt_key_down(); +} + +// ~ ImGui Style #include "../lib/third_party/dear-imgui/imgui_internal.h" - void Set_Custom_Style () { ImGuiStyle* style = &ImGui::GetStyle(); ImVec4* colors = style->Colors; @@ -247,6 +345,23 @@ void ImGui_Debug_Panel () { if (ImGui::Button("Debug_Break()")) { debug_break(); } + if (ImGui::Button("Discover drives (intentional failure)")) { + Win32_Discover_Drives(); + } + + Table* drive_table = &global_win32_state.system_info.drives; + ImGui::Text("drive_table is valid: %d", table_is_valid(drive_table)); + // Most basic Table iterator + s32 current_index = 0; + for (s64 i = 0; i < drive_table->allocated; i += 1) { + Table_Entry* entry = &drive_table->entries[i]; // we should take ptr here if we want to modify? + if (entry->hash > HASH_TABLE_FIRST_VALID_HASH) { + // #TODO: #MOVE THIS + maybe don't check this every frame! + entry->value.is_present = Win32_Drive_Exists(entry->value.label); + ImGui::Text(" > [%d] drive letter: %s (is_present: %d)", current_index, entry->value.label.data, entry->value.is_present); + current_index += 1; + } + } ImGui::End(); } diff --git a/src/imgui_main.cpp b/src/explorer_main.cpp similarity index 80% rename from src/imgui_main.cpp rename to src/explorer_main.cpp index 4b3a92b..78e7614 100644 --- a/src/imgui_main.cpp +++ b/src/explorer_main.cpp @@ -1,13 +1,34 @@ -#include -void ImGui_Application_Win32 () { +#include "Ex1.cpp" + +void Explorer_ImGui_Application_Win32 () { + { // :Win32_Initialize_Extended + Ex1_Register_Global_Hotkeys(); + } ImGui_ImplWin32_EnableDpiAwareness(); f32 main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); - bool success = os_create_window("Main Window", nullptr, true, true, false, ImGui_WndProc); - Assert(success); - HWND hwnd = get_main_window().window; + bool success = false; - // Initialize Direct3D + success = os_create_window("Main Window", nullptr, true, true, false, ImGui_WndProc); + if (!success) { + Assert(false); + ExitProcess(1); + } + get_main_window_pointer()->tray_icon_menu = CreatePopupMenu(); + AppendMenuW(get_main_window_pointer()->tray_icon_menu, MF_STRING, ICON_CONTEXT_MENU_ITEM_ID, L"Exit"); + + HWND hwnd = get_main_window().window; + + success = Win32_Load_Main_Window_Icon("tmp.ico"); + if (!success) { log_error("Failed to load tmp.ico"); } + success = Win32_Load_Main_Window_Icon_Minimized("tmp_min.ico"); + if (!success) { log_error("Failed to load tmp_min.ico"); } + + // :Win32_Initialize_Extended + Win32_Set_Main_Icon(); + Win32_Set_Tray_Icon("Explorer v1"); + Win32_Hide_Tray_Icon(); + if (!CreateDeviceD3D(hwnd)) { CleanupDeviceD3D(); return; @@ -88,11 +109,21 @@ void ImGui_Application_Win32 () { while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); - if (msg.message == WM_QUIT) - done = true; + if (msg.message == WM_QUIT) { + done = true; + } + if (msg.message == WM_HOTKEY) { + switch (msg.wParam) { + case HOTKEY_ID_BRING_TO_FOREGROUND: { + Win32_Bring_Window_To_Foreground(get_main_window_pointer()); + } break; + } + } + } + done = Ex1_check_key_combinations(); + if (done) { + break; } - if (done) - break; // Handle window being minimized or screen locked if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED) { @@ -118,10 +149,7 @@ void ImGui_Application_Win32 () { // Simple dockspace: ImGui::DockSpaceOverViewport(); - { ImGui::Begin("Test panel"); - ImGui::End(); - } - + ImGui_Debug_Panel(); ImGui_Show_Font_Info(); ImGui_Pop_Default_Font(); @@ -143,6 +171,8 @@ void ImGui_Application_Win32 () { HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync // HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED); + + temp_reset_keeping_memory(); // reset_temporary_storage } // while (!done) // Cleanupre @@ -152,4 +182,7 @@ void ImGui_Application_Win32 () { CleanupDeviceD3D(); ::DestroyWindow(hwnd); + + Win32_Hide_Tray_Icon(); + Ex1_Unregister_Global_Hotkeys(); } \ No newline at end of file