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 0000000..236890e Binary files /dev/null and b/extras/icons/tmp.ico differ diff --git a/extras/icons/tmp_min.ico b/extras/icons/tmp_min.ico new file mode 100644 index 0000000..7ebd2ed Binary files /dev/null and b/extras/icons/tmp_min.ico differ diff --git a/lib/Base/Base.h b/lib/Base/Base.h index 17b07d1..a646a07 100644 --- a/lib/Base/Base.h +++ b/lib/Base/Base.h @@ -324,3 +324,4 @@ struct Mutex_Lock_Guard { unlock(mutex); } }; + diff --git a/lib/Base/Hash_Table.h b/lib/Base/Hash_Table.h index fd9f532..0aa99f3 100644 --- a/lib/Base/Hash_Table.h +++ b/lib/Base/Hash_Table.h @@ -40,8 +40,8 @@ struct Table { template 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