summaryrefslogtreecommitdiff
path: root/project-base/src/windows/platform.c
diff options
context:
space:
mode:
authorAldrik Ramaekers <aldrikboy@gmail.com>2024-11-23 22:33:43 +0100
committerAldrik Ramaekers <aldrikboy@gmail.com>2024-11-23 22:33:43 +0100
commitb1e857cf1471d1871a9396696b22fa531da98249 (patch)
tree3923008a8653057698cb339faf6dcfa92e18364b /project-base/src/windows/platform.c
parent106bb7fcadf637cec883648916cc8d19529d6199 (diff)
add projbase to repo
Diffstat (limited to 'project-base/src/windows/platform.c')
-rw-r--r--project-base/src/windows/platform.c1759
1 files changed, 1759 insertions, 0 deletions
diff --git a/project-base/src/windows/platform.c b/project-base/src/windows/platform.c
new file mode 100644
index 0000000..52bf1df
--- /dev/null
+++ b/project-base/src/windows/platform.c
@@ -0,0 +1,1759 @@
+/*
+* BSD 2-Clause “Simplified” License
+* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com
+* All rights reserved.
+*/
+
+#include <windows.h>
+#include <time.h>
+#include <locale.h>
+#include <wingdi.h>
+#include <shlobj.h>
+#include "../external/LooplessSizeMove.c"
+
+struct t_backbuffer
+{
+ s32 width;
+ s32 height;
+ u8 *buffer; // 4bytes color + 1byte depth
+ BITMAPINFO bitmapInfo;
+};
+
+struct t_platform_window
+{
+ HWND window_handle;
+ HDC hdc;
+ HGLRC gl_context;
+ WNDCLASS window_class;
+ s32 flags;
+ s32 style;
+ s32 ex_style;
+ char* title;
+
+ s32 min_width;
+ s32 min_height;
+ s32 max_width;
+ s32 max_height;
+
+ // shared window properties
+ void (*update_func)(platform_window*);
+ void (*resize_func)(platform_window*,u32,u32);
+ keyboard_input keyboard;
+ mouse_input mouse;
+ camera camera;
+ bool icon_loaded;
+ bool do_draw;
+ backbuffer backbuffer;
+ s32 width;
+ s32 height;
+ s32 pre_fullscreen_width;
+ s32 pre_fullscreen_height;
+ bool is_open;
+ bool has_focus;
+ cursor_type curr_cursor_type;
+ cursor_type next_cursor_type;
+ bool vsync_enabled;
+};
+
+extern BOOL GetPhysicallyInstalledSystemMemory(PULONGLONG TotalMemoryInKilobytes);
+
+LARGE_INTEGER perf_frequency;
+static HINSTANCE instance;
+platform_window *current_window_to_handle;
+keyboard_input *current_keyboard_to_handle;
+mouse_input *current_mouse_to_handle;
+
+int cmd_show;
+
+bool platform_get_clipboard(platform_window *window, char *buffer)
+{
+ if (!OpenClipboard(NULL))
+ return false;
+
+ if (!IsClipboardFormatAvailable(CF_UNICODETEXT))
+ {
+ CloseClipboard();
+ return false;
+ }
+
+ wchar_t* clip_str = GetClipboardData(CF_UNICODETEXT);
+ if (!clip_str)
+ {
+ CloseClipboard();
+ return false;
+ }
+
+ WideCharToMultiByte(CP_UTF8, 0, clip_str, -1, buffer, MAX_INPUT_LENGTH ,0,0);
+
+ CloseClipboard();
+ return true;
+}
+
+bool platform_set_clipboard(platform_window *window, char *buffer)
+{
+ HANDLE clipboard_data;
+
+ int char_num = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, 0, 0);
+ wchar_t *convstr = mem_alloc(char_num*2);
+ int result = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, convstr, char_num);
+
+ size_t len = result;
+ size_t size = (len+1) * sizeof(wchar_t);
+ LPSTR dst;
+
+ if (!OpenClipboard(NULL))
+ return false;
+
+ clipboard_data = GlobalAlloc(GMEM_MOVEABLE, size);
+ if (clipboard_data)
+ {
+ dst = GlobalLock(clipboard_data);
+ memmove(dst, convstr, size);
+ dst[len*2] = 0;
+ GlobalUnlock(clipboard_data);
+
+ SetClipboardData(CF_UNICODETEXT, clipboard_data);
+ }
+ else
+ {
+ CloseClipboard();
+ return false;
+ }
+
+ CloseClipboard();
+ return true;
+}
+
+inline void platform_show_alert(char *title, char *message)
+{
+ // not implemented
+}
+
+inline void platform_destroy()
+{
+ _platform_destroy_shared();
+ // memory_print_leaks();
+}
+
+inline void platform_set_cursor(platform_window *window, cursor_type type)
+{
+ if (window->next_cursor_type != type)
+ {
+ window->next_cursor_type = type;
+ }
+}
+
+bool platform_directory_exists(char *path)
+{
+ char tmp[MAX_INPUT_LENGTH];
+ string_copyn(tmp, path, MAX_INPUT_LENGTH);
+
+ s32 len = strlen(tmp);
+ if (tmp[len-1] == '\\')
+ {
+ tmp[len-1] = 0;
+ }
+
+
+ WIN32_FIND_DATA FindFileData;
+ HANDLE handle = FindFirstFile(tmp, &FindFileData) ;
+ int found = handle != INVALID_HANDLE_VALUE;
+ if(found)
+ {
+ //FindClose(&handle); this will crash
+ FindClose(handle);
+ }
+ return found;
+}
+
+static void create_key_tables()
+{
+ keycode_map[0x30] = KEY_0;
+ keycode_map[0x31] = KEY_1;
+ keycode_map[0x32] = KEY_2;
+ keycode_map[0x33] = KEY_3;
+ keycode_map[0x34] = KEY_4;
+ keycode_map[0x35] = KEY_5;
+ keycode_map[0x36] = KEY_6;
+ keycode_map[0x37] = KEY_7;
+ keycode_map[0x38] = KEY_8;
+ keycode_map[0x39] = KEY_9;
+ keycode_map[0x41] = KEY_A;
+ keycode_map[0x42] = KEY_B;
+ keycode_map[0x43] = KEY_C;
+ keycode_map[0x44] = KEY_D;
+ keycode_map[0x45] = KEY_E;
+ keycode_map[0x46] = KEY_F;
+ keycode_map[0x47] = KEY_G;
+ keycode_map[0x48] = KEY_H;
+ keycode_map[0x49] = KEY_I;
+ keycode_map[0x4A] = KEY_J;
+ keycode_map[0x4B] = KEY_K;
+ keycode_map[0x4C] = KEY_L;
+ keycode_map[0x4D] = KEY_M;
+ keycode_map[0x4E] = KEY_N;
+ keycode_map[0x4F] = KEY_O;
+ keycode_map[0x50] = KEY_P;
+ keycode_map[0x51] = KEY_Q;
+ keycode_map[0x52] = KEY_R;
+ keycode_map[0x53] = KEY_S;
+ keycode_map[0x54] = KEY_T;
+ keycode_map[0x55] = KEY_U;
+ keycode_map[0x56] = KEY_V;
+ keycode_map[0x57] = KEY_W;
+ keycode_map[0x58] = KEY_X;
+ keycode_map[0x59] = KEY_Y;
+ keycode_map[0x5A] = KEY_Z;
+
+ keycode_map[VK_OEM_7] = KEY_APOSTROPHE;
+ keycode_map[VK_OEM_102] = KEY_BACKSLASH;
+ keycode_map[VK_OEM_COMMA] = KEY_COMMA;
+ keycode_map[VK_OEM_3] = KEY_GRAVE_ACCENT;
+ keycode_map[VK_OEM_4] = KEY_LEFT_BRACKET;
+ keycode_map[VK_OEM_MINUS] = KEY_MINUS;
+ keycode_map[VK_OEM_PERIOD] = KEY_PERIOD;
+
+ keycode_map[VK_BACK] = KEY_BACKSPACE;
+ keycode_map[VK_DELETE] = KEY_DELETE;
+ keycode_map[VK_END] = KEY_END;
+ keycode_map[VK_RETURN] = KEY_ENTER;
+ keycode_map[VK_ESCAPE] = KEY_ESCAPE;
+ keycode_map[VK_HOME] = KEY_HOME;
+ keycode_map[VK_INSERT] = KEY_INSERT;
+ keycode_map[VK_MENU] = KEY_MENU;
+ keycode_map[VK_NEXT] = KEY_PAGE_DOWN;
+ keycode_map[VK_PRIOR] = KEY_PAGE_UP;
+ keycode_map[VK_PAUSE] = KEY_PAUSE;
+ keycode_map[VK_TAB] = KEY_TAB;
+ keycode_map[VK_CAPITAL] = KEY_CAPS_LOCK;
+ keycode_map[VK_NUMLOCK] = KEY_NUM_LOCK;
+ keycode_map[VK_SCROLL] = KEY_SCROLL_LOCK;
+ keycode_map[0x70] = KEY_F1;
+ keycode_map[0x71] = KEY_F2;
+ keycode_map[0x72] = KEY_F3;
+ keycode_map[0x73] = KEY_F4;
+ keycode_map[0x74] = KEY_F5;
+ keycode_map[0x75] = KEY_F6;
+ keycode_map[0x76] = KEY_F7;
+ keycode_map[0x77] = KEY_F8;
+ keycode_map[0x78] = KEY_F9;
+ keycode_map[0x79] = KEY_F10;
+ keycode_map[0x7A] = KEY_F11;
+ keycode_map[0x7B] = KEY_F12;
+ keycode_map[0x7C] = KEY_F13;
+ keycode_map[0x7D] = KEY_F14;
+ keycode_map[0x7E] = KEY_F15;
+ keycode_map[0x7F] = KEY_F16;
+ keycode_map[0x80] = KEY_F17;
+ keycode_map[0x81] = KEY_F18;
+ keycode_map[0x82] = KEY_F19;
+ keycode_map[0x83] = KEY_F20;
+ keycode_map[0x84] = KEY_F21;
+ keycode_map[0x85] = KEY_F22;
+ keycode_map[0x86] = KEY_F23;
+ keycode_map[0x87] = KEY_F24;
+ keycode_map[0x88] = KEY_LEFT_ALT;
+ keycode_map[VK_CONTROL] = KEY_LEFT_CONTROL;
+ keycode_map[VK_LCONTROL] = KEY_LEFT_CONTROL;
+ keycode_map[VK_LSHIFT] = KEY_LEFT_SHIFT;
+ keycode_map[VK_LWIN] = KEY_LEFT_SUPER;
+ keycode_map[VK_SNAPSHOT] = KEY_PRINT_SCREEN;
+ keycode_map[VK_RMENU] = KEY_RIGHT_ALT;
+ keycode_map[VK_RCONTROL] = KEY_RIGHT_CONTROL;
+ keycode_map[VK_RSHIFT] = KEY_RIGHT_SHIFT;
+ keycode_map[VK_RWIN] = KEY_RIGHT_SUPER;
+ keycode_map[VK_DOWN] = KEY_DOWN;
+ keycode_map[VK_LEFT] = KEY_LEFT;
+ keycode_map[VK_RIGHT] = KEY_RIGHT;
+ keycode_map[VK_UP] = KEY_UP;
+
+ keycode_map[VK_NUMPAD0] = KEY_KP_0;
+ keycode_map[VK_NUMPAD1] = KEY_KP_1;
+ keycode_map[VK_NUMPAD2] = KEY_KP_2;
+ keycode_map[VK_NUMPAD3] = KEY_KP_3;
+ keycode_map[VK_NUMPAD4] = KEY_KP_4;
+ keycode_map[VK_NUMPAD5] = KEY_KP_5;
+ keycode_map[VK_NUMPAD6] = KEY_KP_6;
+ keycode_map[VK_NUMPAD7] = KEY_KP_7;
+ keycode_map[VK_NUMPAD8] = KEY_KP_8;
+ keycode_map[VK_NUMPAD9] = KEY_KP_9;
+ keycode_map[VK_ADD] = KEY_KP_ADD;
+ keycode_map[VK_DECIMAL] = KEY_KP_DECIMAL;
+ keycode_map[VK_DIVIDE] = KEY_KP_DIVIDE;
+ keycode_map[VK_MULTIPLY] = KEY_KP_MULTIPLY;
+ keycode_map[VK_SUBTRACT] = KEY_KP_SUBTRACT;
+}
+
+bool platform_file_exists(char *path)
+{
+ DWORD dwAttrib = GetFileAttributes(path);
+
+ return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
+ !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
+}
+
+void platform_create_config_directory(char *directory)
+{
+ char tmp[MAX_INPUT_LENGTH];
+ if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, tmp)))
+ {
+ string_appendn(tmp, "/", MAX_INPUT_LENGTH);
+ string_appendn(tmp, directory, MAX_INPUT_LENGTH);
+ }
+
+ if (!platform_directory_exists(tmp))
+ {
+ CreateDirectoryA(tmp, NULL);
+ }
+}
+
+char* platform_get_config_save_location(char *buffer, char *directory)
+{
+ if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, buffer)))
+ {
+ string_appendn(buffer, "/", MAX_INPUT_LENGTH);
+ string_appendn(buffer, directory, MAX_INPUT_LENGTH);
+ string_appendn(buffer, "\\config.txt", MAX_INPUT_LENGTH);
+ return buffer;
+ }
+
+ return 0;
+}
+
+void platform_show_message(platform_window *window, char *message, char *title)
+{
+ HWND handle = window ? window->window_handle : NULL;
+ MessageBox(handle, message, title, MB_ICONINFORMATION | MB_OK);
+}
+
+static void _allocate_backbuffer(platform_window *window)
+{
+ if (window->backbuffer.buffer) { mem_free(window->backbuffer.buffer); window->backbuffer.buffer = 0; }
+
+ BITMAPINFO info;
+ info.bmiHeader.biSize = sizeof(BITMAPINFO);
+ info.bmiHeader.biWidth = window->width;
+ info.bmiHeader.biHeight = window->height;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ window->backbuffer.bitmapInfo = info;
+
+ window->backbuffer.width = window->width;
+ window->backbuffer.height = window->height+1;
+
+ s32 bufferMemorySize = (window->backbuffer.width*window->backbuffer.height)*5;
+ window->backbuffer.buffer = mem_alloc(bufferMemorySize);
+}
+
+LRESULT CALLBACK main_window_callback(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ LRESULT result = 0;
+
+ if (message == WM_SIZE)
+ {
+ u32 width = lparam&0xFFFF;
+ u32 height = lparam>>16;
+
+ if (current_window_to_handle->resize_func) {
+ current_window_to_handle->resize_func(current_window_to_handle,
+ width-current_window_to_handle->width, height-current_window_to_handle->height);
+ }
+
+ if (width != 0) {
+ current_window_to_handle->width = width;
+ current_window_to_handle->height = height;
+ }
+
+ if (current_render_driver() == DRIVER_CPU)
+ _allocate_backbuffer(current_window_to_handle);
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_CHAR)
+ {
+ if (current_keyboard_to_handle->take_input)
+ {
+ char buf[5];
+ memset(buf, 0, 5);
+ char *ch = 0;
+
+ wchar_t codep = wparam;
+
+ WideCharToMultiByte(CP_UTF8, 0, &codep, 1, buf, 5 ,0,0);
+
+ if (utf8len(buf) == 1)
+ {
+ char val = buf[0];
+
+ if (current_keyboard_to_handle->input_mode == INPUT_NUMERIC)
+ {
+ if (!(val >= 48 && val <= 57))
+ {
+ ch = 0;
+ }
+ else
+ {
+ snprintf(buf, 2, "%c", val);
+ ch = buf;
+ }
+ }
+ else if (val >= 32 && val <= 126)
+ {
+ snprintf(buf, 5, "%c", val);
+ ch = buf;
+ }
+ }
+
+ if (ch != 0)
+ keyboard_handle_input_string(current_window_to_handle, ch);
+ }
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_KILLFOCUS)
+ {
+ if (current_mouse_to_handle && !(current_window_to_handle->flags & FLAGS_GLOBAL_MOUSE))
+ {
+ current_mouse_to_handle->x = MOUSE_OFFSCREEN;
+ current_mouse_to_handle->y = MOUSE_OFFSCREEN;
+ }
+
+ current_window_to_handle->has_focus = false;
+
+ if (current_keyboard_to_handle)
+ memset(current_keyboard_to_handle->keys, 0, MAX_KEYCODE);
+ }
+ else if (message == WM_SETFOCUS)
+ {
+ current_window_to_handle->has_focus = true;
+ }
+ else if (message == WM_KEYDOWN)
+ {
+ s32 key = wparam;
+
+ current_keyboard_to_handle->keys[keycode_map[key]] = true;
+ current_keyboard_to_handle->input_keys[keycode_map[key]] = true;
+
+ if (current_keyboard_to_handle->take_input)
+ keyboard_handle_input_string(current_window_to_handle, 0);
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_KEYUP)
+ {
+ s32 key = wparam;
+ current_keyboard_to_handle->keys[keycode_map[key]] = false;
+ current_keyboard_to_handle->input_keys[keycode_map[key]] = false;
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_LBUTTONDOWN ||
+ message == WM_RBUTTONDOWN ||
+ message == WM_MBUTTONDOWN ||
+ message == WM_MOUSEWHEEL)
+ {
+ bool is_left_down = wparam & MK_LBUTTON;
+ // bool is_middle_down = wparam & MK_MBUTTON;
+ bool is_right_down = wparam & MK_RBUTTON;
+
+ u64 ev_time = platform_get_time(TIME_FULL, TIME_MILI_S);
+ static u64 last_ev_time = 0;
+
+ if (message == WM_MOUSEWHEEL)
+ {
+ s16 scroll_val = wparam>>16;
+
+ if (scroll_val < 0)
+ current_mouse_to_handle->scroll_state = SCROLL_DOWN;
+ else
+ current_mouse_to_handle->scroll_state = SCROLL_UP;
+ }
+
+ if (!(current_window_to_handle->flags & FLAGS_GLOBAL_MOUSE))
+ {
+ if (is_left_down)
+ {
+ if (ev_time - last_ev_time < 200)
+ {
+ current_mouse_to_handle->left_state |= MOUSE_DOUBLE_CLICK;
+ }
+
+ current_mouse_to_handle->left_state |= MOUSE_DOWN;
+ current_mouse_to_handle->left_state |= MOUSE_CLICK;
+
+ current_mouse_to_handle->total_move_x = 0;
+ current_mouse_to_handle->total_move_y = 0;
+ last_ev_time = ev_time;
+ }
+ }
+
+ if (is_right_down)
+ {
+ current_mouse_to_handle->right_state |= MOUSE_DOWN;
+ current_mouse_to_handle->right_state |= MOUSE_CLICK;
+ }
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_LBUTTONUP ||
+ message == WM_RBUTTONUP ||
+ message == WM_MBUTTONUP)
+ {
+ bool is_left_up = message == WM_LBUTTONUP;
+ bool is_right_up = message == WM_RBUTTONUP;
+ // bool is_middle_up = message == WM_MBUTTONUP;
+
+ if (is_left_up)
+ {
+ current_mouse_to_handle->left_state = MOUSE_RELEASE;
+ }
+ if (is_right_up)
+ {
+ current_mouse_to_handle->right_state = MOUSE_RELEASE;
+ }
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_MOUSEMOVE)
+ {
+ current_window_to_handle->curr_cursor_type = -999;
+
+ TRACKMOUSEEVENT track;
+ track.cbSize = sizeof(track);
+ track.dwFlags = TME_LEAVE;
+ track.hwndTrack = current_window_to_handle->window_handle;
+ TrackMouseEvent(&track);
+
+ current_window_to_handle->do_draw = true;
+ }
+ else if (message == WM_GETMINMAXINFO)
+ {
+ MINMAXINFO *info = (MINMAXINFO*)lparam;
+
+ info->ptMinTrackSize.x = current_window_to_handle->min_width;
+ info->ptMinTrackSize.y = current_window_to_handle->min_height;
+
+ if (current_window_to_handle->max_width)
+ info->ptMaxTrackSize.x = current_window_to_handle->max_width;
+ if (current_window_to_handle->max_height)
+ info->ptMaxTrackSize.y = current_window_to_handle->max_height;
+ }
+ else if (message == WM_DESTROY)
+ {
+ current_window_to_handle->is_open = false;
+ }
+ else if (message == WM_CLOSE)
+ {
+ current_window_to_handle->is_open = false;
+ }
+ else
+ {
+ result = LSMProc(window, message, wparam, lparam); //DefWindowProc
+ }
+
+ return result;
+}
+
+void platform_window_set_title(platform_window *window, char *name)
+{
+ s32 len = strlen(name)+1;
+ wchar_t* wc = mem_alloc(len);
+ mbstowcs(wc, name, len);
+
+ LONG_PTR originalWndProc = GetWindowLongPtrW(window->window_handle, GWLP_WNDPROC);
+ SetWindowLongPtrW(window->window_handle, GWLP_WNDPROC, (LONG_PTR) DefWindowProcW);
+ SetWindowTextW(window->window_handle, wc);
+ SetWindowLongPtrW(window->window_handle, GWLP_WNDPROC, originalWndProc);
+ mem_free(wc);
+}
+
+vec2 platform_get_window_size(platform_window *window)
+{
+ RECT rec;
+ GetWindowRect(window->window_handle, &rec);
+ vec2 res;
+ res.x = rec.right - rec.left;
+ res.y = rec.bottom - rec.top;
+ return res;
+}
+
+void platform_get_focus(platform_window *window)
+{
+ SetFocus(window->window_handle);
+}
+
+void platform_show_window(platform_window *window)
+{
+ ShowWindow(window->window_handle, SW_SHOW);
+}
+
+void platform_hide_window(platform_window *window)
+{
+ ShowWindow(window->window_handle, SW_HIDE);
+}
+
+void platform_toggle_fullscreen(platform_window* window, bool fullscreen)
+{
+ if (fullscreen) {
+ HDC windowHDC = GetDC(window->window_handle);
+ s32 fullscreenWidth = IMP_GetDeviceCaps(windowHDC, DESKTOPHORZRES);
+ s32 fullscreenHeight = IMP_GetDeviceCaps(windowHDC, DESKTOPVERTRES);
+ s32 colourBits = IMP_GetDeviceCaps(windowHDC, BITSPIXEL);
+ s32 refreshRate = IMP_GetDeviceCaps(windowHDC, VREFRESH);
+
+ {
+ window->pre_fullscreen_width = window->width;
+ window->pre_fullscreen_height = window->height;
+ DEVMODE fullscreenSettings;
+
+ EnumDisplaySettings(NULL, 0, &fullscreenSettings);
+ fullscreenSettings.dmPelsWidth = fullscreenWidth;
+ fullscreenSettings.dmPelsHeight = fullscreenHeight;
+ fullscreenSettings.dmBitsPerPel = colourBits;
+ fullscreenSettings.dmDisplayFrequency = refreshRate;
+ fullscreenSettings.dmFields = DM_PELSWIDTH |
+ DM_PELSHEIGHT |
+ DM_BITSPERPEL |
+ DM_DISPLAYFREQUENCY;
+
+ SetWindowLongPtr(window->window_handle, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST);
+ SetWindowLongPtr(window->window_handle, GWL_STYLE, WS_POPUP | WS_VISIBLE);
+ SetWindowPos(window->window_handle, HWND_TOPMOST, 0, 0, fullscreenWidth, fullscreenHeight, SWP_SHOWWINDOW);
+ ChangeDisplaySettings(&fullscreenSettings, CDS_FULLSCREEN);
+ ShowWindow(window->window_handle, SW_MAXIMIZE);
+ }
+ }
+ else {
+ SetWindowLongPtr(window->window_handle, GWL_EXSTYLE, WS_EX_LEFT);
+ SetWindowLongPtr(window->window_handle, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
+ ChangeDisplaySettings(NULL, CDS_RESET);
+ SetWindowPos(window->window_handle, HWND_NOTOPMOST, 0, 0, window->pre_fullscreen_width, window->pre_fullscreen_height+platform_get_titlebar_height(), SWP_SHOWWINDOW);
+ ShowWindow(window->window_handle, SW_RESTORE);
+ }
+
+ log_info("Fullscreen setting changed");
+}
+
+bool _wgl_is_extension_supported(const char *extension)
+{
+ const size_t extlen = strlen(extension);
+ const char *supported = NULL;
+
+ // Try To Use wglGetExtensionStringARB On Current DC, If Possible
+ PROC wglGetExtString = IMP_wglGetProcAddress("wglGetExtensionsStringARB");
+
+ if (wglGetExtString)
+ supported = ((char*(__stdcall*)(HDC))wglGetExtString)(IMP_wglGetCurrentDC());
+
+ // If That Failed, Try Standard Opengl Extensions String
+ if (supported == NULL)
+ supported = (char*)IMP_glGetString(GL_EXTENSIONS);
+
+ // If That Failed Too, Must Be No Extensions Supported
+ if (supported == NULL)
+ return false;
+
+ // Begin Examination At Start Of String, Increment By 1 On False Match
+ for (const char* p = supported; ; p++)
+ {
+ // Advance p Up To The Next Possible Match
+ p = strstr(p, extension);
+
+ if (p == NULL)
+ return false; // No Match
+
+ // Make Sure That Match Is At The Start Of The String Or That
+ // The Previous Char Is A Space, Or Else We Could Accidentally
+ // Match "wglFunkywglExtension" With "wglExtension"
+
+ // Also, Make Sure That The Following Character Is Space Or NULL
+ // Or Else "wglExtensionTwo" Might Match "wglExtension"
+ if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
+ return true; // Match
+ }
+}
+
+int _platform_init_multisample_format(HWND hWnd)
+{
+ // See If The String Exists In WGL!
+ if (!_wgl_is_extension_supported("WGL_ARB_multisample"))
+ {
+ printf("no multisampling supported\n");
+ return 0;
+ }
+
+ PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB =
+ (PFNWGLCHOOSEPIXELFORMATARBPROC)IMP_wglGetProcAddress("wglChoosePixelFormatARB");
+
+ if (!wglChoosePixelFormatARB)
+ {
+ return 0;
+ }
+
+ HDC hDC = GetDC(hWnd);
+ int pixelFormat;
+ bool valid;
+ UINT numFormats;
+ float fAttributes[] = {0,0};
+
+ // These Attributes Are The Bits We Want To Test For In Our Sample
+ // Everything Is Pretty Standard, The Only One We Want To
+ // Really Focus On Is The SAMPLE BUFFERS ARB And WGL SAMPLES
+ // These Two Are Going To Do The Main Testing For Whether Or Not
+ // We Support Multisampling On This Hardware
+ int iAttributes[] = { WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
+ WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
+ WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
+ WGL_COLOR_BITS_ARB,24,
+ WGL_ALPHA_BITS_ARB,8,
+ WGL_DEPTH_BITS_ARB,24,
+ WGL_STENCIL_BITS_ARB,8,
+ WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
+ WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
+ WGL_SAMPLES_ARB, 16 , // Check For 16x Multisampling
+ 0,0};
+ // First We Check To See If We Can Get A Pixel Format For 4 Samples
+ valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
+
+ // if We Returned True, And Our Format Count Is Greater Than 1
+ if (valid && numFormats >= 1)
+ {
+ return pixelFormat;
+ }
+ // Our Pixel Format With 4 Samples Failed, Test For 2 Samples
+ iAttributes[19] = 2;
+ valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
+ if (valid && numFormats >= 1)
+ {
+ return pixelFormat;
+ }
+ return 0;
+}
+
+void platform_toggle_vsync(platform_window* window, bool on)
+{
+ if (IMP_wglGetExtensionsStringEXT == 0) return;
+ if (strstr(IMP_wglGetExtensionsStringEXT(), "WGL_EXT_swap_control") == NULL)
+ return;
+
+ if (IMP_wglSwapIntervalEXT != 0) {
+ window->vsync_enabled = on;
+ IMP_wglSwapIntervalEXT(on);
+ log_info("Vsync setting changed");
+ }
+}
+
+void platform_setup_backbuffer(platform_window *window)
+{
+ static HGLRC share_list = 0;
+ if (current_render_driver() == DRIVER_GL)
+ {
+ if (window->backbuffer.buffer) { mem_free(window->backbuffer.buffer); window->backbuffer.buffer = 0; }
+
+ PIXELFORMATDESCRIPTOR actual_format;
+ // old pixel format selection
+ {
+ s32 suggested_format_index = 0;
+ if (suggested_format_index == 0) {
+ PIXELFORMATDESCRIPTOR format;
+ memset(&format, 0, sizeof(PIXELFORMATDESCRIPTOR));
+ format.nSize = sizeof(PIXELFORMATDESCRIPTOR);
+ format.nVersion = 1;
+ format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ format.cColorBits = 24;
+ format.cAlphaBits = 8;
+ format.cDepthBits = 16;
+ format.iLayerType = PFD_MAIN_PLANE; // PFD_TYPE_RGBA
+ suggested_format_index = IMP_ChoosePixelFormat(window->hdc, &format); // SLOW AF??
+ // https://people.freedesktop.org/~marcheu/extensions/ARB/multisample.html
+ IMP_DescribePixelFormat(window->hdc, suggested_format_index, sizeof(actual_format), &actual_format);
+ }
+
+ IMP_SetPixelFormat(window->hdc, suggested_format_index, &actual_format);
+ }
+
+ window->gl_context = IMP_wglCreateContext(window->hdc);
+
+ if (share_list == 0)
+ {
+ share_list = window->gl_context;
+ }
+ else
+ {
+ IMP_wglShareLists(share_list, window->gl_context);
+ }
+
+ IMP_wglMakeCurrent(window->hdc, window->gl_context);
+
+ // Load wgl specific extensions after gl context has been created.
+ if (IMP_wglSwapIntervalEXT == 0) {
+ __load_fnc_wgl(wglSwapIntervalEXT);
+ __load_fnc_wgl(wglGetSwapIntervalEXT);
+ __load_fnc_wgl(wglGetExtensionsStringEXT);
+ __load_fnc_wgl(wglCreateContextAttribsARB);
+ }
+ platform_toggle_vsync(window, true);
+
+#if 1
+ // Multisampling madness
+ int format = _platform_init_multisample_format(window->window_handle);
+ if (format) {
+ log_info("Multisampling enabled");
+
+ //IMP_wglMakeCurrent(NULL, NULL);
+ //IMP_wglDeleteContext(window->gl_context);
+ ReleaseDC(window->window_handle, window->hdc);
+ DestroyWindow(window->window_handle);
+
+ window->window_handle = CreateWindowEx(window->ex_style,
+ window->window_class.lpszClassName,
+ window->title,
+ window->style,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ window->width,
+ window->height,
+ 0,
+ 0,
+ window->window_class.hInstance,
+ 0);
+ window->hdc = GetDC(window->window_handle);
+
+ PIXELFORMATDESCRIPTOR PFD;
+ IMP_DescribePixelFormat(window->hdc, format, sizeof(PFD), &PFD);
+ IMP_SetPixelFormat(window->hdc, format, &PFD);
+
+ const int major_min = 3, minor_min = 0;
+ int contextAttribs[] = {
+ WGL_CONTEXT_MAJOR_VERSION_ARB, major_min,
+ WGL_CONTEXT_MINOR_VERSION_ARB, minor_min,
+ WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
+ 0
+ };
+
+ window->gl_context = IMP_wglCreateContextAttribsARB(window->hdc, 0, contextAttribs);
+ if (window->gl_context == NULL) {
+ log_info("wglCreateContextAttribsARB() failed.");
+ return;
+ }
+ IMP_wglMakeCurrent(window->hdc, window->gl_context);
+ }
+#endif
+ }
+ else
+ {
+ share_list = 0;
+ IMP_wglMakeCurrent(NULL, NULL);
+ IMP_wglDeleteContext(window->gl_context);
+ window->gl_context = 0;
+
+ _allocate_backbuffer(window);
+ }
+}
+
+void platform_setup_renderer()
+{
+ if (current_render_driver() == DRIVER_GL)
+ {
+ ////// GL SETUP
+ IMP_glDepthMask(GL_TRUE);
+ IMP_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ IMP_glDepthFunc(GL_LEQUAL);
+ IMP_glEnable(GL_DEPTH_TEST);
+ IMP_glAlphaFunc(GL_GREATER, 0.0f);
+ IMP_glEnable(GL_ALPHA_TEST);
+ IMP_glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+ IMP_glEnable(GL_SAMPLE_ALPHA_TO_ONE);
+ IMP_glEnable(GL_MULTISAMPLE);
+ IMP_glEnable(GL_TEXTURE_2D);
+ IMP_glEnable(GL_SCISSOR_TEST);
+ IMP_glEnable(GL_BLEND);
+ //IMP_glEnable(GL_FRAMEBUFFER_SRGB);
+ IMP_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ IMP_glEnable(GL_MULTISAMPLE_ARB);
+
+ IMP_glMatrixMode(GL_TEXTURE);
+ IMP_glLoadIdentity();
+
+ IMP_glMatrixMode(GL_MODELVIEW);
+ IMP_glLoadIdentity();
+ }
+}
+
+platform_window* platform_open_window_ex(char *name, u16 width, u16 height, u16 max_w, u16 max_h, u16 min_w, u16 min_h, s32 flags,
+ void (*update_func)(platform_window* window), void (*resize_func)(platform_window* window, u32, u32))
+{
+ if (width < min_w) width = min_w;
+ if (height < min_h) height = min_h;
+ if (width > max_w) width = max_w;
+ if (height > max_h) width = max_h;
+
+ log_assert(width > 0, "Width of window should be greater than 0");
+ log_assert(height > 0, "Height of window should be greater than 0");
+ log_assert(max_w >= 0, "Maximum width should be greater or equal to 0, where 0 means no limit");
+ log_assert(max_h >= 0, "Maximum height should be greater or equal to 0, where 0 means no limit");
+ log_assert(min_w > 0, "Minimum width should be greater than 0");
+ log_assert(min_h > 0, "Minimum height should be greater than 0");
+ log_assert(update_func, "Update function cannot be 0");
+
+ platform_window* window = mem_alloc(sizeof(platform_window));
+ if (!window) return window;
+ window->has_focus = true;
+ window->update_func = update_func;
+ window->resize_func = resize_func;
+ window->window_handle = 0;
+ window->hdc = 0;
+ window->width = width;
+ window->height = height;
+ window->min_width = min_w;
+ window->min_height = min_h;
+ window->max_width = max_w;
+ window->max_height = max_h;
+ window->curr_cursor_type = -1;
+ window->next_cursor_type = CURSOR_DEFAULT;
+ window->backbuffer.buffer = 0;
+ window->do_draw = true;
+ window->gl_context = 0;
+ window->icon_loaded = false;
+ window->title = name;
+ window->vsync_enabled = false;
+
+ window->keyboard = keyboard_input_create();
+ window->mouse = mouse_input_create();
+ window->camera = (camera){0.0f,0.0f,0.0f};
+
+ current_window_to_handle = window;
+
+ memset(&window->window_class, 0, sizeof(WNDCLASS));
+ window->window_class.style = CS_OWNDC|CS_VREDRAW|CS_HREDRAW;
+ window->window_class.lpfnWndProc = main_window_callback;
+ window->window_class.hInstance = instance;
+ window->window_class.lpszClassName = name;
+ window->window_class.hIcon = LoadIcon(NULL, IDI_WINLOGO);
+ //window->window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
+
+ ATOM success = RegisterClass(&window->window_class);
+ if (!success) return 0;
+
+ int style = WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX;
+ int ex_style = 0;
+
+ if (min_w != max_w && min_h != max_h)
+ style |= WS_SIZEBOX;
+ else
+ style |= WS_THICKFRAME;
+
+ if (flags & FLAGS_BORDERLESS)
+ {
+ style = WS_VISIBLE|WS_POPUP;
+ }
+ if (flags & FLAGS_TOPMOST)
+ {
+ ex_style = WS_EX_TOPMOST;
+ }
+ if (flags & FLAGS_NO_TASKBAR)
+ {
+ ex_style |= WS_EX_TOOLWINDOW;
+ }
+
+ window->style = style;
+ window->ex_style = ex_style;
+ window->window_handle = CreateWindowEx(ex_style,
+ window->window_class.lpszClassName,
+ name,
+ style,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ width,
+ height,
+ 0,
+ 0,
+ instance,
+ 0);
+
+ if (!window->window_handle) return 0;
+
+ if (flags & FLAGS_BORDERLESS)
+ {
+ ShowScrollBar(window->window_handle, SB_VERT, FALSE);
+ ShowScrollBar(window->window_handle, SB_HORZ, FALSE);
+ }
+
+ window->flags = flags;
+
+ window->hdc = GetDC(window->window_handle);
+ if (!window->hdc) return 0;
+
+ platform_setup_backbuffer(window);
+
+ platform_setup_renderer();
+
+ ShowWindow(window->window_handle, cmd_show);
+ if (flags & FLAGS_HIDDEN)
+ ShowWindow(window->window_handle, SW_HIDE);
+ else
+ ShowWindow(window->window_handle, SW_SHOW);
+ window->is_open = true;
+
+ TRACKMOUSEEVENT track;
+ track.cbSize = sizeof(track);
+ track.dwFlags = TME_LEAVE;
+ track.hwndTrack = window->window_handle;
+ TrackMouseEvent(&track);
+ platform_get_focus(window);
+
+ _platform_register_window(window);
+ ShowWindow(window->window_handle, SW_RESTORE);
+ SetForegroundWindow(window->window_handle);
+ return window;
+}
+
+void platform_window_set_size(platform_window *window, u16 width, u16 height)
+{
+ s32 style = GetWindowLong(window->window_handle, GWL_STYLE);
+ BOOL menu = FALSE;
+
+ RECT rec;
+ rec.left = 0;
+ rec.top = 0;
+ rec.right = width;
+ rec.bottom = height;
+ AdjustWindowRectEx(&rec, style, menu, 0);
+ SetWindowPos(window->window_handle,NULL,rec.left,rec.top,rec.right,rec.bottom,SWP_NOMOVE|SWP_NOZORDER);
+
+ if (current_render_driver() == DRIVER_CPU)
+ _allocate_backbuffer(window);
+}
+
+void platform_window_set_position(platform_window *window, u16 x, u16 y)
+{
+ RECT rec;
+ GetWindowRect(window->window_handle, &rec);
+ MoveWindow(window->window_handle, x, y, rec.right-rec.left, rec.bottom-rec.top, FALSE);
+}
+
+bool platform_window_is_valid(platform_window *window)
+{
+ return window && window->hdc && window->window_handle;
+}
+
+bool platform_is_graphical()
+{
+ return true;
+}
+
+s32 platform_get_titlebar_height()
+{
+ return GetSystemMetrics(SM_CYSIZE) + GetSystemMetrics(SM_CYFRAME); // Lets hope this is consistent.
+}
+
+void platform_destroy_window(platform_window *window)
+{
+ if (platform_window_is_valid(window)) {
+ if (current_render_driver() == DRIVER_GL)
+ {
+ IMP_wglMakeCurrent(NULL, NULL);
+ IMP_wglDeleteContext(window->gl_context);
+ }
+ if (window->backbuffer.buffer) { mem_free(window->backbuffer.buffer); window->backbuffer.buffer = 0; }
+
+ ReleaseDC(window->window_handle, window->hdc);
+ CloseWindow(window->window_handle);
+ DestroyWindow(window->window_handle);
+ UnregisterClassA(window->window_class.lpszClassName, instance);
+ _platform_unregister_window(window);
+ window->hdc = 0;
+ window->gl_context = 0;
+ window->window_handle = 0;
+ keyboard_input_destroy(&window->keyboard);
+ }
+}
+
+void _platform_handle_events_for_window(platform_window *window)
+{
+ mouse_input *mouse = &_global_mouse;
+ keyboard_input *keyboard = &_global_keyboard;
+
+ current_window_to_handle = window;
+ current_keyboard_to_handle = keyboard;
+ current_mouse_to_handle = mouse;
+
+#ifndef MODE_TEST
+ mouse->left_state &= ~MOUSE_CLICK;
+ mouse->right_state &= ~MOUSE_CLICK;
+ mouse->left_state &= ~MOUSE_DOUBLE_CLICK;
+ mouse->right_state &= ~MOUSE_DOUBLE_CLICK;
+ mouse->left_state &= ~MOUSE_RELEASE;
+ mouse->right_state &= ~MOUSE_RELEASE;
+ mouse->is_hovering_item = false;
+ memset(keyboard->input_keys, 0, MAX_KEYCODE);
+ mouse->move_x = 0;
+ mouse->move_y = 0;
+ mouse->scroll_state = 0;
+ keyboard->text_changed = false;
+#endif
+
+ // mouse position (including outside of window)
+ current_window_to_handle->has_focus = GetFocus() == current_window_to_handle->window_handle;
+
+#ifndef MODE_TEST
+ if (current_window_to_handle->has_focus || current_window_to_handle->flags & FLAGS_GLOBAL_MOUSE)
+ {
+ if((GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0)
+ {
+ if (!current_mouse_to_handle->last_state_released)
+ {
+ current_mouse_to_handle->left_state = MOUSE_RELEASE;
+ current_mouse_to_handle->last_state_released = true;
+ }
+ }
+ else
+ {
+ current_mouse_to_handle->left_state |= MOUSE_DOWN;
+
+ if (current_mouse_to_handle->last_state_released)
+ {
+ current_mouse_to_handle->left_state |= MOUSE_CLICK;
+ current_mouse_to_handle->last_state_released = false;
+ }
+ }
+
+ RECT rec;
+ GetClientRect(window->window_handle, &rec);
+
+ POINT p;
+ GetCursorPos(&p);
+ ScreenToClient(current_window_to_handle->window_handle, &p);
+
+ mouse->y = p.y;
+ mouse->x = p.x;
+ mouse->inside_of_window = (mouse->x >= 0 && mouse->y >= 0 && mouse->x < window->width && mouse->y < window->height);
+
+#if 0
+ if (current_window_to_handle->flags & FLAGS_GLOBAL_MOUSE)
+ mouse->x = p.x - rec.left;
+ else
+ mouse->x = p.x - rec.left - GetSystemMetrics(SM_CYSIZEFRAME);
+
+ if (current_window_to_handle->flags & FLAGS_BORDERLESS)
+ mouse->y = p.y - rec.top;
+ else
+ mouse->y = p.y - rec.top - GetSystemMetrics(SM_CYSIZE) - GetSystemMetrics(SM_CYFRAME);
+#endif
+ }
+#endif
+
+ MSG message;
+ while(PeekMessageA(&message, window->window_handle, 0, 0, TRUE))
+ {
+ TranslateMessage(&message);
+ SizingCheck(&message);
+ DispatchMessage(&message);
+ }
+
+ if (current_render_driver() == DRIVER_GL)
+ IMP_glViewport(0, 0, window->width, window->height);
+}
+
+void platform_window_swap_buffers(platform_window *window)
+{
+ window->do_draw = false;
+
+ // set cursor if changed
+ if (window->curr_cursor_type != window->next_cursor_type)
+ {
+ char *cursor_shape = 0;
+ switch(window->next_cursor_type)
+ {
+ case CURSOR_DEFAULT: cursor_shape = IDC_ARROW; break;
+ case CURSOR_POINTER: cursor_shape = IDC_HAND; break;
+ case CURSOR_LOADING: cursor_shape = IDC_APPSTARTING; break;
+ case CURSOR_DRAG: cursor_shape = IDC_SIZEWE; break;
+ case CURSOR_TEXT: cursor_shape = IDC_IBEAM; break;
+ }
+
+ HCURSOR cursor = LoadCursorA(NULL, cursor_shape);
+
+ window->curr_cursor_type = window->next_cursor_type;
+ SetCursor(cursor);
+ }
+
+ if (current_render_driver() == DRIVER_CPU)
+ {
+ s32 pixel_count = window->backbuffer.width * window->backbuffer.height;
+ for (s32 i = 0; i < pixel_count; i++)
+ {
+ u8 *buffer_entry = window->backbuffer.buffer + (i*5);
+ memcpy(window->backbuffer.buffer + (i*4), buffer_entry, 4);
+ }
+
+ IMP_StretchDIBits(window->hdc,0,0,window->width,window->height+1,
+ 0,window->backbuffer.height,window->backbuffer.width,
+ -window->backbuffer.height,
+ window->backbuffer.buffer, &window->backbuffer.bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
+ }
+ else
+ {
+ IMP_SwapBuffers(window->hdc);
+ }
+}
+
+s32 platform_get_file_size(char *path)
+{
+ FILE *file = fopen(path, "rb");
+ if (!file) return -1;
+
+ fseek(file, 0 , SEEK_END);
+ int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ fclose(file);
+ return length;
+}
+
+file_content platform_read_file_content(char *path, const char *mode)
+{
+ file_content result;
+ result.content = 0;
+ result.content_length = 0;
+ result.file_error = 0;
+
+ FILE *file = fopen(path, mode);
+ if (!file)
+ {
+ if (errno == EMFILE)
+ result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS;
+ else if (errno == ENFILE)
+ result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM;
+ else if (errno == EACCES)
+ result.file_error = FILE_ERROR_NO_ACCESS;
+ else if (errno == EPERM)
+ result.file_error = FILE_ERROR_NO_ACCESS;
+ else if (errno == ENOENT)
+ result.file_error = FILE_ERROR_NOT_FOUND;
+ else if (errno == ECONNABORTED)
+ result.file_error = FILE_ERROR_CONNECTION_ABORTED;
+ else if (errno == ECONNREFUSED)
+ result.file_error = FILE_ERROR_CONNECTION_REFUSED;
+ else if (errno == ENETDOWN)
+ result.file_error = FILE_ERROR_NETWORK_DOWN;
+ else
+ {
+ result.file_error = FILE_ERROR_GENERIC;
+ printf("ERROR: %d\n", errno);
+ }
+
+ goto done_failure;
+ }
+
+ fseek(file, 0 , SEEK_END);
+ int length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ s32 length_to_alloc = length+1;
+
+ result.content = mem_alloc(length_to_alloc);
+ if (!result.content) goto done;
+
+ memset(result.content, 0, length);
+ s32 read_result = fread(result.content, 1, length, file);
+ if (read_result == 0 && length != 0)
+ {
+ mem_free(result.content);
+ result.content = 0;
+ return result;
+ }
+
+ result.content_length = read_result;
+
+ ((char*)result.content)[length] = 0;
+
+ done:
+ fclose(file);
+ done_failure:
+ return result;
+}
+
+bool platform_delete_file(char *path)
+{
+ return remove(path) == 0;
+}
+
+bool platform_write_file_content(char *path, const char *mode, char *buffer, s32 len)
+{
+ bool result = false;
+
+ FILE *file = fopen(path, mode);
+
+ if (!file)
+ {
+ goto done_failure;
+ }
+ else
+ {
+ fwrite(buffer, 1, len, file);
+ //fprintf(file, buffer);
+ }
+
+ //done:
+ fclose(file);
+ done_failure:
+ return result;
+}
+
+void platform_destroy_file_content(file_content *content)
+{
+ log_assert(content, "Content is null");
+ mem_free(content->content);
+}
+
+bool platform_get_active_directory(char *buffer)
+{
+ return GetCurrentDirectory(MAX_INPUT_LENGTH, buffer);
+}
+
+bool platform_set_active_directory(char *path)
+{
+ return SetCurrentDirectory(path);
+}
+
+void platform_list_files_block(array *list, char *start_dir, array filters, bool recursive, memory_bucket *bucket, bool include_directories, bool *is_cancelled, search_info *info)
+{
+ log_assert(list, "List is null");
+ s32 len = 0;
+ char *matched_filter = 0;
+
+ char *subdirname_buf;
+ if (bucket)
+ subdirname_buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
+ else
+ subdirname_buf = mem_alloc(MAX_INPUT_LENGTH);
+
+ char *start_dir_fix;
+ if (bucket)
+ start_dir_fix = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
+ else
+ start_dir_fix = mem_alloc(MAX_INPUT_LENGTH);
+ snprintf(start_dir_fix, MAX_INPUT_LENGTH, "%s*", start_dir);
+
+ char *start_dir_clean;
+ if (bucket)
+ start_dir_clean = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
+ else
+ start_dir_clean = mem_alloc(MAX_INPUT_LENGTH);
+ string_copyn(start_dir_clean, start_dir, MAX_INPUT_LENGTH);
+
+ WIN32_FIND_DATAA file_info;
+ HWND handle = FindFirstFileA(start_dir_fix, &file_info);
+
+ if (!bucket)
+ mem_free(start_dir_fix);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ return;
+ }
+
+ do
+ {
+ if (*is_cancelled) break;
+ char *name = file_info.cFileName;
+
+ // symbolic link is not allowed..
+ if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
+ continue;
+
+
+ if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
+ continue;
+
+ if (include_directories)
+ {
+ if ((len = platform_filter_matches(&filters, name,
+ &matched_filter)) && len != -1)
+ {
+ // is file
+ char *buf;
+ if (bucket)
+ buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
+ else
+ buf = mem_alloc(MAX_INPUT_LENGTH);
+ snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, name);
+
+ found_file f;
+ f.path = buf;
+
+ if (bucket)
+ f.matched_filter= memory_bucket_reserve(bucket, len+1);
+ else
+ f.matched_filter= mem_alloc(len+1);
+
+ string_copyn(f.matched_filter, matched_filter, len+1);
+
+ mutex_lock(&list->mutex);
+ array_push_size(list, (uint8_t*)&f, sizeof(found_file));
+ mutex_unlock(&list->mutex);
+ }
+ }
+
+ if (recursive)
+ {
+ if (info) info->dir_count++;
+
+ string_copyn(subdirname_buf, start_dir_clean, MAX_INPUT_LENGTH);
+ string_appendn(subdirname_buf, name, MAX_INPUT_LENGTH);
+ string_appendn(subdirname_buf, "\\", MAX_INPUT_LENGTH);
+
+ // is directory
+ platform_list_files_block(list, subdirname_buf, filters, recursive, bucket, include_directories, is_cancelled, info);
+ }
+ }
+ else if ((file_info.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ||
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ||
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ||
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE))
+ {
+ if (info) info->file_count++;
+
+ if ((len = platform_filter_matches(&filters, name,
+ &matched_filter)) && len != -1)
+ {
+ // is file
+ char *buf;
+ if (bucket)
+ buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH);
+ else
+ buf = mem_alloc(MAX_INPUT_LENGTH);
+
+ snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, name);
+
+ found_file f;
+ f.path = buf;
+
+ if (bucket)
+ f.matched_filter = memory_bucket_reserve(bucket, len+1);
+ else
+ f.matched_filter = mem_alloc(len+1);
+
+ string_copyn(f.matched_filter, matched_filter, len+1);
+
+ mutex_lock(&list->mutex);
+ array_push_size(list, (uint8_t*)&f, sizeof(found_file));
+ mutex_unlock(&list->mutex);
+ }
+ }
+ }
+ while (FindNextFile(handle, &file_info) != 0);
+
+ if (!bucket)
+ mem_free(start_dir_clean);
+
+ FindClose(handle);
+}
+
+static void* platform_open_file_dialog_implementation(void *data)
+{
+ open_dialog_args *args = data;
+
+ OPENFILENAME info;
+ info.lStructSize = sizeof(OPENFILENAME);
+ info.hwndOwner = NULL;
+ info.hInstance = NULL;
+
+ char filter[MAX_INPUT_LENGTH];
+ memset(filter, 0, MAX_INPUT_LENGTH);
+
+ if (args->file_filter)
+ {
+ string_copyn(filter, args->file_filter, MAX_INPUT_LENGTH);
+ filter[strlen(filter)] = 0;
+ filter[strlen(filter)+1] = 0;
+ info.lpstrFilter = filter;
+ }
+ else
+ {
+ info.lpstrFilter = NULL;
+ }
+
+ char szFile[MAX_INPUT_LENGTH];
+ memset(szFile, 0, MAX_INPUT_LENGTH);
+
+ info.lpstrCustomFilter = NULL;
+ info.nMaxCustFilter = MAX_INPUT_LENGTH;
+ info.nFilterIndex = 0;
+ info.lpstrFile = (char*)szFile;
+ info.nMaxFile = MAX_INPUT_LENGTH;
+
+ info.lpstrDefExt = args->default_save_file_extension;
+
+ info.lpstrFileTitle = NULL;
+ info.lpstrInitialDir = args->start_path;
+ info.lpstrTitle = NULL;
+
+ if (args->type == SAVE_FILE)
+ {
+ info.Flags = OFN_EXTENSIONDIFFERENT | OFN_OVERWRITEPROMPT;
+ IMP_GetSaveFileNameA(&info);
+ string_copyn(args->buffer, info.lpstrFile, MAX_INPUT_LENGTH);
+ }
+ else if (args->type == OPEN_DIRECTORY)
+ {
+ BROWSEINFOA inf;
+ PIDLIST_ABSOLUTE result = SHBrowseForFolderA(&inf);
+ if (!result) return 0;
+
+ SHGetPathFromIDListA(result, args->buffer);
+ }
+ else if (args->type == OPEN_FILE)
+ {
+ info.Flags = OFN_FILEMUSTEXIST;
+ IMP_GetOpenFileNameA(&info);
+ string_copyn(args->buffer, info.lpstrFile, MAX_INPUT_LENGTH);
+ }
+
+ return 0;
+}
+
+void *platform_open_file_dialog_block(void *arg)
+{
+ platform_open_file_dialog_implementation(arg);
+ mem_free(arg);
+ return 0;
+}
+
+char *platform_get_full_path(char *file)
+{
+ char *buf = mem_alloc(MAX_INPUT_LENGTH);
+ if (!GetFullPathNameA(file, MAX_INPUT_LENGTH, buf, NULL))
+ {
+ buf[0] = 0;
+ }
+
+ return buf;
+}
+
+void platform_open_url(char *command)
+{
+ platform_run_command(command);
+}
+
+void platform_run_command(char *command)
+{
+ // might be start instead of open
+ ShellExecuteA(NULL, "open", command, NULL, NULL, SW_SHOWDEFAULT);
+}
+
+void platform_window_make_current(platform_window *window)
+{
+ if (current_render_driver() == DRIVER_GL)
+ IMP_wglMakeCurrent(window->hdc, window->gl_context);
+}
+
+void platform_init(int argc, char **argv, char* config_path)
+{
+ setlocale(LC_ALL, "en_US.UTF-8");
+
+ QueryPerformanceFrequency(&perf_frequency);
+ create_key_tables();
+
+ instance = GetModuleHandle(NULL);
+ cmd_show = argc;
+
+ _platform_init_shared(argc, argv, config_path);
+}
+
+void platform_set_icon(platform_window *window, image *img)
+{
+ if (!img->loaded) return;
+ if (!window->icon_loaded)
+ window->icon_loaded = true;
+ else
+ return;
+
+ // NOTE only works with bmps currently; remove #if 0 to enable png again..
+ // we should probably change png color alignment so png and bmp data is the same in memory
+ BYTE *bmp;
+ s32 data_len = img->width * img->height * 4;
+ s32 total_len = data_len + 40 * 4;
+
+ bmp = mem_alloc(total_len);
+
+ struct {
+ int32_t header_size, width, geight;
+ int16_t color_plane, bits_per_pixel;
+ int32_t compression_mode, img_length, obsolete[4];
+ } bmp_header = {40, img->width, img->height * 2, 1, 32, BI_RGB, data_len, {0,0,0,0} };
+
+ memcpy(bmp, &bmp_header, 40);
+
+ s32 index = 0;
+ for (s32 y = img->height-1; y >= 0; y--)
+ {
+ for (s32 x = 0; x < img->width; x++)
+ {
+ s32 img_pixel = *(((s32*)img->data+(x+(y*img->width))));
+
+#if 0
+ // 0xAABBGGRR
+ s32 a = (img_pixel>>24) & 0x000000FF;
+ s32 b = (img_pixel>>16) & 0x000000FF;
+ s32 g = (img_pixel>> 8) & 0x000000FF;
+ s32 r = (img_pixel>> 0) & 0x000000FF;
+
+ //s32 c = (r << 24) | (g << 16) | (b << 8) | (a << 0);
+ s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0);
+#endif
+ s32 c = img_pixel;
+ memcpy(bmp+40+(index*4), &c, 4);
+
+ ++index;
+ }
+ }
+
+ HICON icon = CreateIconFromResourceEx(bmp, total_len, TRUE, 0x00030000, img->width, img->height, LR_DEFAULTCOLOR);
+
+ SendMessage(window->window_handle, WM_SETICON, ICON_SMALL, (LPARAM)icon);
+ SendMessage(window->window_handle, WM_SETICON, ICON_BIG, (LPARAM)icon);
+
+ mem_free(bmp);
+
+ if (!icon)
+ printf("Failed to load icon, error code: %ld.\n", GetLastError());
+}
+
+u64 platform_get_time(time_type time_type, time_precision precision)
+{
+ LARGE_INTEGER counter;
+ QueryPerformanceCounter(&counter);
+
+ double sec = counter.QuadPart / (double)(perf_frequency.QuadPart);
+
+ //printf("%I64d %I64d %f\n", counter.QuadPart, perf_frequency.QuadPart, sec);
+
+ double val = sec;
+
+ if (precision == TIME_NS)
+ {
+ return val*1000000000;
+ }
+ if (precision == TIME_US)
+ {
+ return val*1000000;
+ }
+ if (precision == TIME_MILI_S)
+ {
+ return val*1000;
+ }
+ if (precision == TIME_S)
+ {
+ return val;
+ }
+ return val;
+}
+
+s32 platform_get_memory_size()
+{
+ u64 result;
+ GetPhysicallyInstalledSystemMemory(&result);
+ return result;
+}
+
+s32 platform_get_cpu_count()
+{
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+
+ return info.dwNumberOfProcessors;
+}
+
+#if 0
+bool platform_send_http_request(char *url, char *params, char *response_buffer)
+{
+ // https://www.unix.com/programming/187337-c-http-get-request-using-sockets.html
+
+ bool response = true;
+ HINTERNET hIntSession = 0;
+ HINTERNET hHttpSession = 0;
+ HINTERNET hHttpRequest = 0;
+
+ hIntSession = InternetOpen("Text-Search", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
+ if (!hIntSession) goto failure;
+
+ hHttpSession = InternetConnect(hIntSession, url, 80, 0, 0, INTERNET_SERVICE_HTTP, 0, 0);
+ if (!hHttpSession) goto failure;
+
+ hHttpRequest = HttpOpenRequest(
+ hHttpSession,
+ "GET",
+ params,
+ 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
+
+
+ char* szHeaders = "Content-Type: application/json";
+ char szReq[1024] = "";
+ if(!HttpSendRequest(hHttpRequest, szHeaders, strlen(szHeaders), szReq, strlen(szReq))) {
+ goto failure;
+ }
+
+ DWORD dwRead=0;
+ while(InternetReadFile(hHttpRequest, response_buffer, MAX_INPUT_LENGTH-1, &dwRead) && dwRead) {
+ response_buffer[dwRead] = 0;
+ dwRead=0;
+ }
+
+ goto done;
+
+ failure:
+ printf("failure");
+ response = false;
+
+ done:
+ InternetCloseHandle(hHttpRequest);
+ InternetCloseHandle(hHttpSession);
+ InternetCloseHandle(hIntSession);
+
+ return response;
+}
+
+bool platform_get_mac_address(char *buffer, s32 buf_size)
+{
+ PIP_ADAPTER_INFO pAdapterInfo;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ UINT i;
+
+ ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
+ pAdapterInfo = mem_alloc(sizeof(IP_ADAPTER_INFO));
+
+ if (!pAdapterInfo) return false;
+
+ if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
+ mem_free(pAdapterInfo);
+ pAdapterInfo = mem_alloc(ulOutBufLen);
+ if (!pAdapterInfo) return false;
+ }
+
+ if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
+ pAdapter = pAdapterInfo;
+
+ if (pAdapter) {
+ for (i = 0; i < pAdapter->AddressLength; i++) {
+ if (i == (pAdapter->AddressLength - 1))
+ buffer += sprintf(buffer, "%.2X", (int)pAdapter->Address[i]);
+ else
+ buffer += sprintf(buffer, "%.2X-", (int)pAdapter->Address[i]);
+ }
+
+ if (pAdapterInfo) mem_free(pAdapterInfo);
+ return true;
+ }
+ }
+
+ return false;
+}
+#endif \ No newline at end of file