diff --git a/src/os/win32/os_core_win32.c b/src/os/win32/os_core_win32.c new file mode 100644 index 0000000..ffb5879 --- /dev/null +++ b/src/os/win32/os_core_win32.c @@ -0,0 +1,256 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//~ + +#pragma comment(lib, "kernel32") +#pragma comment(lib, "user32") +#pragma comment(lib, "gdi32") + + +#define OS_W32_MSG_CREATE_WINDOW (WM_USER + 0x1337) +#define OS_W32_MSG_DESTROY_WINDOW (WM_USER + 0x1338) +global DWORD MainThreadID; +global HWND g_service_window_handle; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//~ + +internal void * +os_reserve(U64 size) +{ + void *result = VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE); + return result; +} + +internal B32 +os_commit(void *ptr, U64 size) +{ + B32 result = (VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != 0); + return result; +} + +internal void +os_decommit(void *ptr, U64 size) +{ + VirtualFree(ptr, size, MEM_DECOMMIT); +} + +internal void +os_release(void *ptr, U64 size) +{ + // NOTE(anton): Don't need to use size for freeing on Win32, but the API + // should have it for other platforms. + VirtualFree(ptr, 0, MEM_RELEASE); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//~ + +internal LRESULT CALLBACK +ServiceWndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) +{ + /* NOTE(casey): This is not really a window handler per se, it's actually just + a remote thread call handler. Windows only really has blocking remote thread + calls if you register a WndProc for them, so that's what we do. + + This handles CREATE_DANGEROUS_WINDOW and DESTROY_DANGEROUS_WINDOW, which are + just calls that do CreateWindow and DestroyWindow here on this thread when + some other thread wants that to happen. + */ + + LRESULT result = 0; + + switch(Message) + { + case OS_W32_MSG_CREATE_WINDOW: + { + OS_W32_WndParam *wndParam = (OS_W32_WndParam *)WParam; + result = (LRESULT)CreateWindowExW(wndParam->dwExStyle, + wndParam->lpClassName, + wndParam->lpWindowName, + wndParam->dwStyle, + wndParam->X, + wndParam->Y, + wndParam->nWidth, + wndParam->nHeight, + wndParam->hWndParent, + wndParam->hMenu, + wndParam->hInstance, + wndParam->lpParam); + + } break; + + case OS_W32_MSG_DESTROY_WINDOW: + { + DestroyWindow((HWND)WParam); + } break; + + default: + { + result = DefWindowProcW(Window, Message, WParam, LParam); + } break; + } + + + return result; +} + +internal LRESULT CALLBACK +DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) +{ + /* NOTE(casey): This is an example of an actual window procedure. It doesn't do anything + but forward things to the main thread, because again, all window messages now occur + on the message thread, and presumably we would rather handle everything there. You + don't _have_ to do that - you could choose to handle some of the messages here. + But if you did, you would have to actually think about whether there are race conditions + with your main thread and all that. So just PostThreadMessageW()'ing everything gets + you out of having to think about it. + */ + + LRESULT Result = 0; + + switch (Message) + { + // NOTE(casey): Mildly annoying, if you want to specify a window, you have + // to snuggle the params yourself, because Windows doesn't let you forward + // a god damn window message even though the program IS CALLED WINDOWS. It's + // in the name! Let me pass it! + case WM_CLOSE: + { + PostThreadMessageW(MainThreadID, Message, (WPARAM)Window, LParam); + } break; + + // NOTE(casey): Anything you want the application to handle, forward to the main thread + // here. + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_DESTROY: + case WM_CHAR: + { + PostThreadMessageW(MainThreadID, Message, WParam, LParam); + } break; + + default: + { + Result = DefWindowProcW(Window, Message, WParam, LParam); + } break; + } + + return Result; +} + +internal DWORD WINAPI +MainThreadEntryPoint(LPVOID Param) +{ + /* NOTE(Casey): This is your app code. Basically you just do everything the same, + but instead of calling CreateWindow/DestroyWindow, you use SendMessage to + do it on the other thread, using the CREATE_DANGEROUS_WINDOW and DESTROY_DANGEROUS_WINDOW + user messages. Otherwise, everything proceeds as normal. + */ + HWND ServiceWindow = (HWND)Param; + g_service_window_handle = ServiceWindow; + + WNDCLASSEXW WindowClass = {0}; + WindowClass.cbSize = sizeof(WindowClass); + WindowClass.lpfnWndProc = &DisplayWndProc; + WindowClass.hInstance = GetModuleHandleW(NULL); + WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION); + WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW); + WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + WindowClass.lpszClassName = L"Dangerous Class"; + RegisterClassExW(&WindowClass); + + OS_W32_WndParam wndParam = {0}; + wndParam.dwExStyle = 0;; + wndParam.lpClassName = WindowClass.lpszClassName; + wndParam.lpWindowName = L"Dangerous Window"; + wndParam.dwStyle = WS_OVERLAPPEDWINDOW|WS_VISIBLE; + wndParam.X = CW_USEDEFAULT; + wndParam.Y = CW_USEDEFAULT; + wndParam.nWidth = CW_USEDEFAULT; + wndParam.nHeight = CW_USEDEFAULT; + wndParam.hInstance = WindowClass.hInstance; + + // Create initial window + HWND ThisWouldBeTheHandleIfYouCared = (HWND)SendMessageW(ServiceWindow, + OS_W32_MSG_CREATE_WINDOW, + (WPARAM)&wndParam, + 0); + + entry_point(); + + + LOG("Exited main program. Terminating \n"); + ExitProcess(0); +} + +int +WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + + /* NOTE(casey): At startup, you create one hidden window used to handle requests + to create or destroy windows. There's nothing special about this window; it + only exists because Windows doesn't have a way to do a remote thread call without + a window handler. You could instead just do this with your own synchronization + primitives if you wanted - this is just the easiest way to do it on Windows + because they've already built it for you. + */ + + LOG("Entered WinMain\n"); + + WNDCLASSEXW WindowClass = {0}; + WindowClass.cbSize = sizeof(WindowClass); + WindowClass.lpfnWndProc = &ServiceWndProc; + WindowClass.hInstance = GetModuleHandleW(NULL); + WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION); + WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW); + WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + WindowClass.lpszClassName = L"DTCClass"; + RegisterClassExW(&WindowClass); + + HWND ServiceWindow = CreateWindowExW(0, WindowClass.lpszClassName, L"DTCService", 0, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, WindowClass.hInstance, 0); + + // NOTE(casey): Once the service window is created, you can start the main thread, + // which is where all your app code would actually happen. + CreateThread(0, 0, MainThreadEntryPoint, ServiceWindow, 0, &MainThreadID); + + // NOTE(casey): This thread can just idle for the rest of the run, forwarding + // messages to the main thread that it thinks the main thread wants. + DWORD exitCode; + for(;;) + { + + MSG Message; + GetMessageW(&Message, 0, 0, 0); + TranslateMessage(&Message); + if((Message.message == WM_CHAR) || + (Message.message == WM_KEYDOWN) || + (Message.message == WM_QUIT) || + (Message.message == WM_SIZE)) + { + PostThreadMessageW(MainThreadID, Message.message, Message.wParam, Message.lParam); + } + else + { + DispatchMessageW(&Message); + } + + } + + return 0; +} + + +internal void +OS_W32_log_debug(const char *fmt_string, ...) +{ + char buffer[1024]; + va_list args; + va_start(args, fmt_string); + _vsnprintf(buffer, sizeof(buffer) / sizeof(char), fmt_string, args); + va_end(args); + OutputDebugStringA(buffer); +} diff --git a/src/os/win32/os_core_win32.h b/src/os/win32/os_core_win32.h new file mode 100644 index 0000000..ac10cd5 --- /dev/null +++ b/src/os/win32/os_core_win32.h @@ -0,0 +1,31 @@ +#ifndef OS_CORE_WIN32_H +#define OS_CORE_WIN32_H + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//~ +#define WIN32_LEAN_AND_MEAN +#include + +// Temp struct for launching window on separate thread. +// Trying to learn from https://github.com/cmuratori/dtc/blob/main/dtc.cpp +typedef struct OS_W32_WndParam OS_W32_WndParam; +struct OS_W32_WndParam +{ + DWORD dwExStyle; + LPCWSTR lpClassName; + LPCWSTR lpWindowName; + DWORD dwStyle; + S32 X; + S32 Y; + S32 nWidth; + S32 nHeight; + HWND hWndParent; + HMENU hMenu; + HINSTANCE hInstance; + LPVOID lpParam; +}; + + +internal void OS_W32_log_debug(const char *fmt_string, ...); + +#endif /* OS_CORE_WIN32_H */ \ No newline at end of file