4coder/code/platform_win32/win32_4ed_functions.cpp

575 lines
19 KiB
C++

/*
* Mr. 4th Dimention - Allen Webster
*
* 18.07.2017
*
* General win32 functions
*
*/
// TOP
internal b32
system_file_can_be_made(Arena *scratch, u8 *filename){
HANDLE file = CreateFile_utf8(scratch, filename, FILE_APPEND_DATA, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
b32 result = false;
if (file != INVALID_HANDLE_VALUE){
CloseHandle(file);
result = true;
}
return(result);
}
//
// Memory
//
struct Memory_Annotation_Tracker_Node{
Memory_Annotation_Tracker_Node *next;
Memory_Annotation_Tracker_Node *prev;
String_Const_u8 location;
u64 size;
};
struct Memory_Annotation_Tracker{
Memory_Annotation_Tracker_Node *first;
Memory_Annotation_Tracker_Node *last;
i32 count;
};
global Memory_Annotation_Tracker memory_tracker = {};
global CRITICAL_SECTION memory_tracker_mutex;
internal void*
win32_memory_allocate_extended(void *base, u64 size, String_Const_u8 location){
u64 adjusted_size = size + 64;
void *result = VirtualAlloc(base, (SIZE_T)adjusted_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
Memory_Annotation_Tracker_Node *node = (Memory_Annotation_Tracker_Node*)result;
EnterCriticalSection(&memory_tracker_mutex);
zdll_push_back(memory_tracker.first, memory_tracker.last, node);
memory_tracker.count += 1;
LeaveCriticalSection(&memory_tracker_mutex);
node->location = location;
node->size = size;
return(node + 1);
}
internal void
win32_memory_free_extended(void *ptr){
Memory_Annotation_Tracker_Node *node = (Memory_Annotation_Tracker_Node*)ptr;
node -= 1;
EnterCriticalSection(&memory_tracker_mutex);
zdll_remove(memory_tracker.first, memory_tracker.last, node);
memory_tracker.count -= 1;
LeaveCriticalSection(&memory_tracker_mutex);
VirtualFree(node, 0, MEM_RELEASE);
}
internal
system_memory_allocate_sig(){
return(win32_memory_allocate_extended(0, size, location));
}
internal
system_memory_set_protection_sig(){
DWORD protect = 0;
switch (flags & 0x7){
case 0: protect = PAGE_NOACCESS; break;
case MemProtect_Read: protect = PAGE_READONLY; break;
case MemProtect_Write: /* below */
case MemProtect_Write|MemProtect_Read: protect = PAGE_READWRITE; break;
case MemProtect_Execute: protect = PAGE_EXECUTE; break;
case MemProtect_Execute|MemProtect_Read: protect = PAGE_EXECUTE_READ; break;
case MemProtect_Execute|MemProtect_Write: /* below */
case MemProtect_Execute|MemProtect_Write|MemProtect_Read: protect = PAGE_EXECUTE_READWRITE; break;
}
Memory_Annotation_Tracker_Node *node = (Memory_Annotation_Tracker_Node*)ptr;
node -= 1;
DWORD old_protect = 0;
b32 result = VirtualProtect(node, (SIZE_T)size, protect, &old_protect);
return(result);
}
internal
system_memory_free_sig(){
win32_memory_free_extended(ptr);
}
internal
system_memory_annotation_sig(){
Memory_Annotation result = {};
EnterCriticalSection(&memory_tracker_mutex);
for (Memory_Annotation_Tracker_Node *node = memory_tracker.first;
node != 0;
node = node->next){
Memory_Annotation_Node *r_node = push_array(arena, Memory_Annotation_Node, 1);
sll_queue_push(result.first, result.last, r_node);
result.count += 1;
r_node->location = node->location;
r_node->address = node + 1;
r_node->size = node->size;
}
LeaveCriticalSection(&memory_tracker_mutex);
return(result);
}
//
// 4ed path
//
extern "C" BOOL CALL_CONVENTION
GetUserProfileDirectoryW(HANDLE hToken, LPWSTR lpProfileDir, LPDWORD lpcchSize);
global String_Const_u8 w32_override_user_directory = {};
internal
system_get_path_sig(){
String_Const_u8 result = {};
switch (path_code){
case SystemPath_CurrentDirectory:
{
DWORD size = GetCurrentDirectory_utf8(arena, 0, 0);
u8 *out = push_array(arena, u8, size);
size = GetCurrentDirectory_utf8(arena, size, out);
result = SCu8(out, size);
}break;
case SystemPath_Binary:
{
local_persist b32 has_stashed_4ed_path = false;
if (!has_stashed_4ed_path){
has_stashed_4ed_path = true;
local_const i32 binary_path_capacity = KB(32);
u8 *memory = (u8*)system_memory_allocate(binary_path_capacity, string_u8_litexpr(file_name_line_number));
i32 size = GetModuleFileName_utf8(arena, 0, memory, binary_path_capacity);
Assert(size <= binary_path_capacity - 1);
win32vars.binary_path = SCu8(memory, size);
win32vars.binary_path = string_remove_last_folder(win32vars.binary_path);
win32vars.binary_path.str[win32vars.binary_path.size] = 0;
}
result = push_string_copy(arena, win32vars.binary_path);
}break;
case SystemPath_UserDirectory:
{
if (w32_override_user_directory.size == 0){
HANDLE current_process_token = GetCurrentProcessToken();
DWORD size = 0;
GetUserProfileDirectoryW(current_process_token, 0, &size);
u16 *buffer_u16 = push_array(arena, u16, size);
if (GetUserProfileDirectoryW(current_process_token, (WCHAR*)buffer_u16, &size)){
String8 path = string_u8_from_string_u16(arena, SCu16(buffer_u16, size), StringFill_NullTerminate).string;
result = push_stringf(arena, "%.*s\\4coder\\", string_expand(path));
}
}
else{
result = w32_override_user_directory;
}
}break;
}
return(result);
}
//
// Files
//
internal String_Const_u8
win32_remove_unc_prefix_characters(String_Const_u8 path){
if (string_match(string_prefix(path, 7), string_u8_litexpr("\\\\?\\UNC"))){
#if 0
// TODO(allen): Why no just do
path = string_skip(path, 7);
path.str[0] = '\\';
// ?
#endif
path.size -= 7;
block_copy(path.str, path.str + 7, path.size);
path.str[0] = '\\';
}
else if (string_match(string_prefix(path, 4), string_u8_litexpr("\\\\?\\"))){
// TODO(allen): Same questions essentially.
path.size -= 4;
block_copy(path.str, path.str + 4, path.size);
}
return(path);
}
internal
system_get_canonical_sig(){
String_Const_u8 result = {};
if ((character_is_alpha(string_get_character(name, 0)) &&
string_get_character(name, 1) == ':') ||
string_match(string_prefix(name, 2), string_u8_litexpr("\\\\"))){
u8 *c_name = push_array(arena, u8, name.size + 1);
block_copy(c_name, name.str, name.size);
c_name[name.size] = 0;
HANDLE file = CreateFile_utf8(arena, c_name, GENERIC_READ, 0, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if (file != INVALID_HANDLE_VALUE){
DWORD capacity = GetFinalPathNameByHandle_utf8(arena, file, 0, 0, 0);
u8 *buffer = push_array(arena, u8, capacity);
DWORD length = GetFinalPathNameByHandle_utf8(arena, file, buffer, capacity, 0);
if (length > 0 && buffer[length - 1] == 0){
length -= 1;
}
result = SCu8(buffer, length);
result = win32_remove_unc_prefix_characters(result);
CloseHandle(file);
}
else{
String_Const_u8 path = string_remove_front_of_path(name);
String_Const_u8 front = string_front_of_path(name);
u8 *c_path = push_array(arena, u8, path.size + 1);
block_copy(c_path, path.str, path.size);
c_path[path.size] = 0;
HANDLE dir = CreateFile_utf8(arena, c_path, FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, 0);
if (dir != INVALID_HANDLE_VALUE){
DWORD capacity = GetFinalPathNameByHandle_utf8(arena, dir, 0, 0, 0);
u8 *buffer = push_array(arena, u8, capacity + front.size + 1);
DWORD length = GetFinalPathNameByHandle_utf8(arena, dir, buffer, capacity, 0);
if (length > 0 && buffer[length - 1] == 0){
length -= 1;
}
buffer[length] = '\\';
length += 1;
block_copy(buffer + length, front.str, front.size);
length += (DWORD)front.size;
result = SCu8(buffer, length);
result = win32_remove_unc_prefix_characters(result);
CloseHandle(dir);
}
}
}
return(result);
}
internal File_Attribute_Flag
win32_convert_file_attribute_flags(DWORD dwFileAttributes){
File_Attribute_Flag result = {};
MovFlag(dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY, result, FileAttribute_IsDirectory);
return(result);
}
internal u64
win32_u64_from_u32_u32(u32 hi, u32 lo){
return( (((u64)hi) << 32) | ((u64)lo) );
}
internal u64
win32_u64_from_filetime(FILETIME time){
return(win32_u64_from_u32_u32(time.dwHighDateTime, time.dwLowDateTime));
}
internal File_Attributes
win32_file_attributes_from_HANDLE(HANDLE file){
BY_HANDLE_FILE_INFORMATION info = {};
GetFileInformationByHandle(file, &info);
File_Attributes result = {};
result.size = win32_u64_from_u32_u32(info.nFileSizeHigh, info.nFileSizeLow);
result.last_write_time = win32_u64_from_filetime(info.ftLastWriteTime);
result.flags = win32_convert_file_attribute_flags(info.dwFileAttributes);
return(result);
}
internal
system_get_file_list_sig(){
File_List result = {};
String_Const_u8 search_pattern = {};
if (character_is_slash(string_get_character(directory, directory.size - 1))){
search_pattern = push_u8_stringf(arena, "%.*s*", string_expand(directory));
}
else{
search_pattern = push_u8_stringf(arena, "%.*s\\*", string_expand(directory));
}
WIN32_FIND_DATA find_data = {};
HANDLE search = FindFirstFile_utf8(arena, search_pattern.str, &find_data);
if (search != INVALID_HANDLE_VALUE){
File_Info *first = 0;
File_Info *last = 0;
i32 count = 0;
for (;;){
String_Const_u16 file_name_utf16 = SCu16(find_data.cFileName);
if (!(string_match(file_name_utf16, string_u16_litexpr(L".")) ||
string_match(file_name_utf16, string_u16_litexpr(L"..")))){
String_Const_u8 file_name = string_u8_from_string_u16(arena, file_name_utf16,
StringFill_NullTerminate).string;
File_Info *info = push_array(arena, File_Info, 1);
sll_queue_push(first, last, info);
count += 1;
info->file_name = file_name;
info->attributes.size = win32_u64_from_u32_u32(find_data.nFileSizeHigh,
find_data.nFileSizeLow);
info->attributes.last_write_time = win32_u64_from_filetime(find_data.ftLastWriteTime);
info->attributes.flags = win32_convert_file_attribute_flags(find_data.dwFileAttributes);
}
if (!FindNextFile(search, &find_data)){
break;
}
}
result.infos = push_array(arena, File_Info*, count);
result.count = count;
i32 counter = 0;
for (File_Info *node = first;
node != 0;
node = node->next){
result.infos[counter] = node;
counter += 1;
}
FindClose(search);
}
return(result);
}
internal
system_quick_file_attributes_sig(){
WIN32_FILE_ATTRIBUTE_DATA info = {};
File_Attributes result = {};
if (GetFileAttributesEx_utf8String(scratch, file_name, GetFileExInfoStandard, &info)){
result.size = ((u64)info.nFileSizeHigh << 32LL) | ((u64)info.nFileSizeLow);
result.last_write_time = ((u64)info.ftLastWriteTime.dwHighDateTime << 32LL) | ((u64)info.ftLastWriteTime.dwLowDateTime);
result.flags = win32_convert_file_attribute_flags(info.dwFileAttributes);
}
return(result);
}
internal
system_load_handle_sig(){
b32 result = false;
HANDLE file = CreateFile_utf8(scratch, (u8*)file_name, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (file != INVALID_HANDLE_VALUE){
*(HANDLE*)out = file;
result = true;
}
return(result);
}
internal
system_load_attributes_sig(){
HANDLE file = *(HANDLE*)(&handle);
return(win32_file_attributes_from_HANDLE(file));
}
internal
system_load_file_sig(){
HANDLE file = *(HANDLE*)(&handle);
DWORD read_size = 0;
b32 result = false;
if (ReadFile(file, buffer, size, &read_size, 0)){
if (read_size == size){
result = true;
}
}
return(result);
}
internal
system_load_close_sig(){
b32 result = false;
HANDLE file = *(HANDLE*)(&handle);
if (CloseHandle(file)){
result = true;
}
return(result);
}
internal
system_save_file_sig(){
File_Attributes result = {};
HANDLE file = CreateFile_utf8(scratch, (u8*)file_name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (file != INVALID_HANDLE_VALUE){
u64 written_total = 0;
b32 success = true;
for (;written_total < data.size;){
DWORD read_size = max_u32;
DWORD write_size = 0;
if ((data.size - written_total) < max_u32){
read_size = (DWORD)data.size;
}
if (!WriteFile(file, data.str + written_total, read_size, &write_size, 0)){
success = false;
break;
}
written_total += write_size;
}
if (success){
result = win32_file_attributes_from_HANDLE(file);
}
CloseHandle(file);
}
return(result);
}
////////////////////////////////
internal ARGB_Color
swap_r_and_b(ARGB_Color a){
ARGB_Color result = a & 0xff00ff00;
result |= ((a >> 16) & 0xff);
result |= ((a & 0xff) << 16);
return(result);
}
internal ARGB_Color
int_color_from_colorref(COLORREF ref, ARGB_Color alpha_from){
ARGB_Color rgb = swap_r_and_b(ref & 0xffffff);
ARGB_Color result = ((0xff000000 & alpha_from) | rgb);
return(result);
}
internal UINT_PTR CALLBACK
color_picker_hook(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam){
UINT_PTR result = 0;
switch(Message)
{
// TODO(allen): review
case WM_INITDIALOG:
{
CHOOSECOLORW *win32_params = (CHOOSECOLORW *)LParam;
Color_Picker *picker = (Color_Picker*)win32_params->lCustData;
SetWindowLongPtr(Window, GWLP_USERDATA, (LONG_PTR)LParam);
Scratch_Block scratch(win32vars.tctx);
String_u16 temp = string_u16_from_string_u8(scratch, picker->title, StringFill_NullTerminate);
SetWindowTextW(Window, (LPCWSTR)temp.str);
} break;
case WM_CTLCOLORSTATIC:
{
// NOTE(casey): I can't believe I'm 42 years old and I still have to do
// this fucking crap. Microsoft is so fucking fired every god damn day.
// Would it have killed you to update rgbResult continuously, or at least
// provide a GetCurrentColor() call???
//
// Anyway, since the color picker doesn't tell us when the color is
// changed, what we do is watch for messages that repaint the color
// swatch, which is dialog id 0x2c5, and then we sample it to see what
// color it is. No, I'm not fucking kidding, that's what we do.
HWND swatch_window = (HWND)LParam;
if(GetDlgCtrlID(swatch_window) == 0x2c5)
{
CHOOSECOLORW *win32_params =
(CHOOSECOLORW *)GetWindowLongPtr(Window, GWLP_USERDATA);
if(win32_params)
{
Color_Picker *picker = (Color_Picker*)win32_params->lCustData;
RECT rect;
GetClientRect(swatch_window, &rect);
HDC swatch_dc = (HDC)WParam;
COLORREF Probe = GetPixel(swatch_dc,
(rect.left + rect.right)/4,
(rect.top + rect.bottom)/2);
ARGB_Color new_color =
int_color_from_colorref(Probe, *picker->dest);
if(*picker->dest != new_color)
{
*picker->dest = new_color;
system_signal_step(0);
}
}
}
} break;
default:
{
#if 0
// NOTE(casey): Enable this if you want to dump the color edit dialog messages to the debug log
short Temp[256];
wsprintf((LPWSTR)Temp, L"%u 0x%x 0x%x\n", Message, WParam, LParam);
OutputDebugStringW((LPWSTR)Temp);
#endif
} break;
}
return(result);
}
// TODO(allen): review
internal DWORD WINAPI
color_picker_thread(LPVOID Param)
{
Color_Picker *picker = (Color_Picker*)Param;
ARGB_Color color = 0;
if (picker->dest){
color = *picker->dest;
}
COLORREF custom_colors[16] = {};
CHOOSECOLORW win32_params = {};
win32_params.lStructSize = sizeof(win32_params);
//win32_params.hwndOwner = win32vars.window_handle;
win32_params.hInstance = win32vars.window_handle;
win32_params.rgbResult = swap_r_and_b(color) & 0xffffff;
win32_params.lpCustColors = custom_colors;
win32_params.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR | CC_ENABLEHOOK;
win32_params.lCustData = (LPARAM)picker;
win32_params.lpfnHook = color_picker_hook;
if (ChooseColorW(&win32_params)){
color = int_color_from_colorref(win32_params.rgbResult, color);
}
if(picker->dest){
*picker->dest = color;
}
if (picker->finished){
*picker->finished = true;
}
system_memory_free(picker, sizeof(*picker));
return(0);
}
internal
system_open_color_picker_sig(){
// TODO(allen): review
// NOTE(casey): Because this is going to be used by a semi-permanent thread, we need to
// copy it to system memory where it can live as long as it wants, no matter what we do
// over here on the 4coder threads.
Color_Picker *perm = (Color_Picker*)system_memory_allocate(sizeof(Color_Picker), string_u8_litexpr(file_name_line_number));
*perm = *picker;
HANDLE ThreadHandle = CreateThread(0, 0, color_picker_thread, perm, 0, 0);
CloseHandle(ThreadHandle);
}
internal
system_get_screen_scale_factor_sig(){
return(win32vars.screen_scale_factor);
}
// BOTTOM