#define AL_LOG_SECTION "win32" #include #include "window_win32.h" #include "common.h" #include "keys.h" // All extra Windows headers come after . #include #ifndef NAUNET_WIN32_COMPAT_MODE #include #endif #if defined STELA_API_VULKAN #include "vk_common.h" #elif defined STELA_API_OPENGL #include "gl_common.h" // https://registry.khronos.org/OpenGL/api/GL/wgl.h #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 #define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 #define STELA_GL_API 1 #define STELA_GLES_API 2 #define STELA_GL_BIT 1 #define STELA_GLES2_BIT 2 #define STELA_GLES3_BIT 3 #define STELA_GL_CORE_PROFILE_BIT 1 #include "gl_versions.h" PASTE_GL_VERSIONS() #endif f64 stl_scale_scroll(f64 v, f64 factor) { SHORT Value = 0; al_memcpy(&Value, &v, sizeof(SHORT)); return Value / (WHEEL_DELTA * factor); } void stl_set_output_discovered_callback(void (*output_discovered)(void *, struct stl_monitor *), void *userdata) { (void)output_discovered; (void)userdata; } // https://github.com/cmuratori/dtc static LPCWSTR DangerousClass = L"Dangerous Class"; struct the_baby { DWORD dwExStyle; LPCWSTR lpClassName; LPCWSTR lpWindowName; DWORD dwStyle; s32 X; s32 Y; s32 nWidth; s32 nHeight; HWND hWndParent; HMENU hMenu; HINSTANCE hInstance; LPVOID lpParam; }; // Service messages. #define CREATE_DANGEROUS_WINDOW (WM_USER + 0x1337) #define DESTROY_DANGEROUS_WINDOW (WM_USER + 0x1338) #define EXIT_PROCESS (WM_USER + 0x1339) // Window messages. #define WAKE_WINDOW (WM_USER + 0x133a) #define SET_CURSOR (WM_USER + 0x133b) static DWORD MainThreadID; static struct stl_win32_global Global = { 0 }; static LRESULT CALLBACK ServiceWndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) { LRESULT Result = 0; switch (Message) { case CREATE_DANGEROUS_WINDOW: { struct the_baby *Baby = (struct the_baby *)WParam; Result = (LRESULT)CreateWindowExW(Baby->dwExStyle, Baby->lpClassName, Baby->lpWindowName, Baby->dwStyle, Baby->X, Baby->Y, Baby->nWidth, Baby->nHeight, Baby->hWndParent, Baby->hMenu, Baby->hInstance, Baby->lpParam); break; } case DESTROY_DANGEROUS_WINDOW: { struct stl_window_win32 *w32 = (struct stl_window_win32 *)WParam; if (!w32->Closed && !DestroyWindow(w32->Window)) { log_error("Failed to destroy window (0x%lx).", GetLastError()); } UnregisterClassW(DangerousClass, Global.ModuleHandle); break; } case EXIT_PROCESS: PostQuitMessage(WParam); break; default: Result = DefWindowProcW(Window, Message, WParam, LParam); break; } return Result; } static DWORD WINAPI MainThread(LPVOID Param) { (void)Param; s32 ret = Global.main(Global.argc, Global.argv, Global.extra); SendMessageW(Global.ServiceWindow, EXIT_PROCESS, (WPARAM)ret, 0); return 0; } static void set_keycodes_for_win32(void) { al_memset(Global.keycodes, 0, sizeof(Global.keycodes)); Global.keycodes[0x30] = STELA_KEY_0; Global.keycodes[0x31] = STELA_KEY_1; Global.keycodes[0x32] = STELA_KEY_2; Global.keycodes[0x33] = STELA_KEY_3; Global.keycodes[0x34] = STELA_KEY_4; Global.keycodes[0x35] = STELA_KEY_5; Global.keycodes[0x36] = STELA_KEY_6; Global.keycodes[0x37] = STELA_KEY_7; Global.keycodes[0x38] = STELA_KEY_8; Global.keycodes[0x39] = STELA_KEY_9; Global.keycodes[0x41] = STELA_KEY_A; Global.keycodes[0x42] = STELA_KEY_B; Global.keycodes[0x43] = STELA_KEY_C; Global.keycodes[0x44] = STELA_KEY_D; Global.keycodes[0x45] = STELA_KEY_E; Global.keycodes[0x46] = STELA_KEY_F; Global.keycodes[0x47] = STELA_KEY_G; Global.keycodes[0x48] = STELA_KEY_H; Global.keycodes[0x49] = STELA_KEY_I; Global.keycodes[0x4a] = STELA_KEY_J; Global.keycodes[0x4b] = STELA_KEY_K; Global.keycodes[0x4c] = STELA_KEY_L; Global.keycodes[0x4d] = STELA_KEY_M; Global.keycodes[0x4e] = STELA_KEY_N; Global.keycodes[0x4f] = STELA_KEY_O; Global.keycodes[0x50] = STELA_KEY_P; Global.keycodes[0x51] = STELA_KEY_Q; Global.keycodes[0x52] = STELA_KEY_R; Global.keycodes[0x53] = STELA_KEY_S; Global.keycodes[0x54] = STELA_KEY_T; Global.keycodes[0x55] = STELA_KEY_U; Global.keycodes[0x56] = STELA_KEY_V; Global.keycodes[0x57] = STELA_KEY_W; Global.keycodes[0x58] = STELA_KEY_X; Global.keycodes[0x59] = STELA_KEY_Y; Global.keycodes[0x5a] = STELA_KEY_Z; Global.keycodes[VK_SPACE] = STELA_KEY_SPACE; Global.keycodes[VK_OEM_7] = STELA_KEY_APOSTROPHE; Global.keycodes[VK_OEM_COMMA] = STELA_KEY_COMMA; Global.keycodes[VK_OEM_PERIOD] = STELA_KEY_PERIOD; Global.keycodes[VK_OEM_2] = STELA_KEY_SLASH; Global.keycodes[VK_OEM_5] = STELA_KEY_BACKSLASH; Global.keycodes[VK_OEM_3] = STELA_KEY_GRAVE_ACCENT; Global.keycodes[VK_OEM_MINUS] = STELA_KEY_MINUS; Global.keycodes[VK_OEM_4] = STELA_KEY_LEFT_BRACKET; Global.keycodes[VK_OEM_6] = STELA_KEY_RIGHT_BRACKET; Global.keycodes[VK_OEM_1] = STELA_KEY_SEMICOLON; Global.keycodes[VK_OEM_PLUS] = STELA_KEY_EQUAL; Global.keycodes[VK_RETURN] = STELA_KEY_RETURN; Global.keycodes[VK_BACK] = STELA_KEY_BACKSPACE; Global.keycodes[VK_TAB] = STELA_KEY_TAB; Global.keycodes[VK_ESCAPE] = STELA_KEY_ESCAPE; Global.keycodes[VK_INSERT] = STELA_KEY_INSERT; Global.keycodes[VK_DELETE] = STELA_KEY_DELETE; Global.keycodes[VK_HOME] = STELA_KEY_HOME; Global.keycodes[VK_END] = STELA_KEY_END; Global.keycodes[VK_PRIOR] = STELA_KEY_PAGE_UP; Global.keycodes[VK_NEXT] = STELA_KEY_PAGE_DOWN; Global.keycodes[VK_SNAPSHOT] = STELA_KEY_PRINT_SCREEN; Global.keycodes[VK_PAUSE] = STELA_KEY_PAUSE; Global.keycodes[VK_CAPITAL] = STELA_KEY_CAPS_LOCK; Global.keycodes[VK_SCROLL] = STELA_KEY_SCROLL_LOCK; Global.keycodes[VK_NUMLOCK] = STELA_KEY_NUM_LOCK; Global.keycodes[VK_LEFT] = STELA_KEY_LEFT; Global.keycodes[VK_RIGHT] = STELA_KEY_RIGHT; Global.keycodes[VK_UP] = STELA_KEY_UP; Global.keycodes[VK_DOWN] = STELA_KEY_DOWN; Global.keycodes[VK_F1] = STELA_KEY_F1; Global.keycodes[VK_F2] = STELA_KEY_F2; Global.keycodes[VK_F3] = STELA_KEY_F3; Global.keycodes[VK_F4] = STELA_KEY_F4; Global.keycodes[VK_F5] = STELA_KEY_F5; Global.keycodes[VK_F6] = STELA_KEY_F6; Global.keycodes[VK_F7] = STELA_KEY_F7; Global.keycodes[VK_F8] = STELA_KEY_F8; Global.keycodes[VK_F9] = STELA_KEY_F9; Global.keycodes[VK_F10] = STELA_KEY_F10; Global.keycodes[VK_F11] = STELA_KEY_F11; Global.keycodes[VK_F12] = STELA_KEY_F12; Global.keycodes[VK_F13] = STELA_KEY_F13; Global.keycodes[VK_F14] = STELA_KEY_F14; Global.keycodes[VK_F15] = STELA_KEY_F15; Global.keycodes[VK_F16] = STELA_KEY_F16; Global.keycodes[VK_F17] = STELA_KEY_F17; Global.keycodes[VK_F18] = STELA_KEY_F18; Global.keycodes[VK_F19] = STELA_KEY_F19; Global.keycodes[VK_F20] = STELA_KEY_F20; Global.keycodes[VK_F21] = STELA_KEY_F21; Global.keycodes[VK_F22] = STELA_KEY_F22; Global.keycodes[VK_F23] = STELA_KEY_F23; Global.keycodes[VK_F24] = STELA_KEY_F24; Global.keycodes[VK_NUMPAD0] = STELA_KEY_KP_0; Global.keycodes[VK_NUMPAD1] = STELA_KEY_KP_1; Global.keycodes[VK_NUMPAD2] = STELA_KEY_KP_2; Global.keycodes[VK_NUMPAD3] = STELA_KEY_KP_3; Global.keycodes[VK_NUMPAD4] = STELA_KEY_KP_4; Global.keycodes[VK_NUMPAD5] = STELA_KEY_KP_5; Global.keycodes[VK_NUMPAD6] = STELA_KEY_KP_6; Global.keycodes[VK_NUMPAD7] = STELA_KEY_KP_7; Global.keycodes[VK_NUMPAD8] = STELA_KEY_KP_8; Global.keycodes[VK_NUMPAD9] = STELA_KEY_KP_9; Global.keycodes[VK_ADD] = STELA_KEY_KP_ADD; Global.keycodes[VK_SUBTRACT] = STELA_KEY_KP_SUBTRACT; Global.keycodes[VK_MULTIPLY] = STELA_KEY_KP_MULTIPLY; Global.keycodes[VK_DIVIDE] = STELA_KEY_KP_DIVIDE; Global.keycodes[VK_DECIMAL] = STELA_KEY_KP_DECIMAL; Global.keycodes[VK_OEM_PLUS] = STELA_KEY_KP_EQUAL; Global.keycodes[VK_RETURN] = STELA_KEY_KP_RETURN; Global.keycodes[VK_LCONTROL] = STELA_KEY_LEFT_CONTROL; Global.keycodes[VK_LMENU] = STELA_KEY_LEFT_ALT; Global.keycodes[VK_LSHIFT] = STELA_KEY_LEFT_SHIFT; Global.keycodes[VK_RCONTROL] = STELA_KEY_RIGHT_CONTROL; Global.keycodes[VK_RMENU] = STELA_KEY_RIGHT_ALT; Global.keycodes[VK_RSHIFT] = STELA_KEY_RIGHT_SHIFT; Global.keycodes[VK_LWIN] = STELA_KEY_LEFT_SUPER; Global.keycodes[VK_RWIN] = STELA_KEY_RIGHT_SUPER; } //MONITORENUMPROC MonitorInfoProc; BOOL __stdcall MonitorInfoProc(HMONITOR Monitor, HDC DeviceContext, LPRECT Rect, LPARAM UserData) { struct stl_monitor_win32 *monitor = al_alloc_object(struct stl_monitor_win32); (void)DeviceContext; (void)UserData; monitor->M.width = Rect->right - Rect->left; monitor->M.height = Rect->bottom - Rect->top; monitor->M.scale = 1; monitor->X = Rect->left; monitor->Y = Rect->top; monitor->Handle = Monitor; al_array_push(Global.monitors, (struct stl_monitor *)monitor); return TRUE; } bool stl_global_init(bool skip_graphics) { (void)skip_graphics; Global.ModuleHandle = GetModuleHandleW(NULL); HMODULE NTModule = LoadLibraryA("ntdll.dll"); if (!NTModule) return false; if (GetProcAddress(NTModule, "wine_get_version") != NULL) { log_info("Wine detected; using Windows-incompatible window move method."); Global.IsWine = TRUE; } else { Global.IsWine = FALSE; } #ifdef STELA_API_VULKAN Global.VKExtensions = al_calloc(2, sizeof(const char *const *)); Global.VKExtensions[0] = "VK_KHR_surface"; Global.VKExtensions[1] = "VK_KHR_win32_surface"; #endif al_array_init(Global.monitors); EnumDisplayMonitors(NULL, NULL, MonitorInfoProc, 0); Global.Cursors[STELA_CURSOR_NORMAL] = LoadCursor(NULL, IDC_ARROW); Global.Cursors[STELA_CURSOR_MOVE] = LoadCursor(NULL, IDC_HAND); Global.Cursors[STELA_CURSOR_CROSSHAIR] = LoadCursor(NULL, IDC_CROSS); set_keycodes_for_win32(); return true; } s32 stl_global_get_poll_fd() { al_assert_and_return(-1); } void stl_global_process_events() { al_assert(false); } s32 stl_swallow_main(u32 argc, str *argv, s32 (*main)(u32, str *, void *), void *extra) { WNDCLASSEXW WindowClass = { 0 }; WindowClass.cbSize = sizeof(WindowClass); WindowClass.lpfnWndProc = &ServiceWndProc; WindowClass.hInstance = Global.ModuleHandle; WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION); WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW); WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); WindowClass.lpszClassName = L"DTCClass"; RegisterClassExW(&WindowClass); Global.ServiceWindow = CreateWindowExW(0, WindowClass.lpszClassName, L"DTCService", 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, WindowClass.hInstance, 0); if (!Global.ServiceWindow) { log_error("Failed to create service window (0x%lx).", GetLastError()); UnregisterClassW(WindowClass.lpszClassName, Global.ModuleHandle); return EXIT_FAILURE; } Global.argc = argc; Global.argv = argv; Global.extra = extra; Global.main = main; HANDLE Thread = CreateThread(0, 0, MainThread, NULL, 0, &MainThreadID); for (;;) { MSG Message; GetMessageW(&Message, 0, 0, 0); TranslateMessage(&Message); if (Message.message == WM_QUIT) { WaitForSingleObject(Thread, INFINITE); CloseHandle(Thread); DestroyWindow(Global.ServiceWindow); UnregisterClassW(WindowClass.lpszClassName, Global.ModuleHandle); return Message.wParam; } DispatchMessageW(&Message); } } void stl_global_close(void) { #ifdef STELA_API_VULKAN al_free(Global.VKExtensions); #endif struct stl_monitor *monitor; al_array_foreach(Global.monitors, i, monitor) { al_free((struct stl_window_win32 *)monitor); } al_array_free(Global.monitors); } #ifdef STELA_API_OPENGL // https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions static void *GetAnyGLFuncAddress(const char *Name) { void *p = (void *)wglGetProcAddress(Name); if (p == 0 || p == (void *)0x1 || p == (void *)0x2 || p == (void *)0x3 || p == (void *)-1) { HMODULE GLModule = LoadLibraryA("opengl32.dll"); p = (void *)GetProcAddress(GLModule, Name); } return p; } typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const s32 *); typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(s32); #endif //#define LOG_ALL_WND_MESSAGES #ifdef LOG_ALL_WND_MESSAGES // https://en.wikichip.org/wiki/List_of_Windows_Messages_-_Win32 #define MESSAGE_CASE_STR(value) case value: return #value; static const char *WndMessageString(UINT Message) { switch (Message) { MESSAGE_CASE_STR(WM_NULL) MESSAGE_CASE_STR(WM_CREATE) MESSAGE_CASE_STR(WM_DESTROY) MESSAGE_CASE_STR(WM_MOVE) MESSAGE_CASE_STR(WM_SIZE) MESSAGE_CASE_STR(WM_ACTIVATE) MESSAGE_CASE_STR(WM_SETFOCUS) MESSAGE_CASE_STR(WM_KILLFOCUS) MESSAGE_CASE_STR(WM_ENABLE) MESSAGE_CASE_STR(WM_SETREDRAW) MESSAGE_CASE_STR(WM_SETTEXT) MESSAGE_CASE_STR(WM_GETTEXT) MESSAGE_CASE_STR(WM_GETTEXTLENGTH) MESSAGE_CASE_STR(WM_PAINT) MESSAGE_CASE_STR(WM_CLOSE) MESSAGE_CASE_STR(WM_QUERYENDSESSION) MESSAGE_CASE_STR(WM_QUERYOPEN) MESSAGE_CASE_STR(WM_ENDSESSION) MESSAGE_CASE_STR(WM_QUIT) MESSAGE_CASE_STR(WM_ERASEBKGND) MESSAGE_CASE_STR(WM_SYSCOLORCHANGE) MESSAGE_CASE_STR(WM_SHOWWINDOW) MESSAGE_CASE_STR(WM_WININICHANGE) //MESSAGE_CASE_STR(WM_SETTINGCHANGE) MESSAGE_CASE_STR(WM_DEVMODECHANGE) MESSAGE_CASE_STR(WM_ACTIVATEAPP) MESSAGE_CASE_STR(WM_FONTCHANGE) MESSAGE_CASE_STR(WM_TIMECHANGE) MESSAGE_CASE_STR(WM_CANCELMODE) MESSAGE_CASE_STR(WM_SETCURSOR) MESSAGE_CASE_STR(WM_MOUSEACTIVATE) MESSAGE_CASE_STR(WM_CHILDACTIVATE) MESSAGE_CASE_STR(WM_QUEUESYNC) MESSAGE_CASE_STR(WM_GETMINMAXINFO) MESSAGE_CASE_STR(WM_PAINTICON) MESSAGE_CASE_STR(WM_ICONERASEBKGND) MESSAGE_CASE_STR(WM_NEXTDLGCTL) MESSAGE_CASE_STR(WM_SPOOLERSTATUS) MESSAGE_CASE_STR(WM_DRAWITEM) MESSAGE_CASE_STR(WM_MEASUREITEM) MESSAGE_CASE_STR(WM_DELETEITEM) MESSAGE_CASE_STR(WM_VKEYTOITEM) MESSAGE_CASE_STR(WM_CHARTOITEM) MESSAGE_CASE_STR(WM_SETFONT) MESSAGE_CASE_STR(WM_GETFONT) MESSAGE_CASE_STR(WM_SETHOTKEY) MESSAGE_CASE_STR(WM_GETHOTKEY) MESSAGE_CASE_STR(WM_QUERYDRAGICON) MESSAGE_CASE_STR(WM_COMPAREITEM) MESSAGE_CASE_STR(WM_GETOBJECT) MESSAGE_CASE_STR(WM_COMPACTING) MESSAGE_CASE_STR(WM_COMMNOTIFY) MESSAGE_CASE_STR(WM_WINDOWPOSCHANGING) MESSAGE_CASE_STR(WM_WINDOWPOSCHANGED) MESSAGE_CASE_STR(WM_POWER) MESSAGE_CASE_STR(WM_COPYDATA) MESSAGE_CASE_STR(WM_CANCELJOURNAL) MESSAGE_CASE_STR(WM_NOTIFY) MESSAGE_CASE_STR(WM_INPUTLANGCHANGEREQUEST) MESSAGE_CASE_STR(WM_INPUTLANGCHANGE) MESSAGE_CASE_STR(WM_TCARD) MESSAGE_CASE_STR(WM_HELP) MESSAGE_CASE_STR(WM_USERCHANGED) MESSAGE_CASE_STR(WM_NOTIFYFORMAT) MESSAGE_CASE_STR(WM_CONTEXTMENU) MESSAGE_CASE_STR(WM_STYLECHANGING) MESSAGE_CASE_STR(WM_STYLECHANGED) MESSAGE_CASE_STR(WM_DISPLAYCHANGE) MESSAGE_CASE_STR(WM_GETICON) MESSAGE_CASE_STR(WM_SETICON) MESSAGE_CASE_STR(WM_NCCREATE) MESSAGE_CASE_STR(WM_NCDESTROY) MESSAGE_CASE_STR(WM_NCCALCSIZE) MESSAGE_CASE_STR(WM_NCHITTEST) MESSAGE_CASE_STR(WM_NCPAINT) MESSAGE_CASE_STR(WM_NCACTIVATE) MESSAGE_CASE_STR(WM_GETDLGCODE) MESSAGE_CASE_STR(WM_SYNCPAINT) MESSAGE_CASE_STR(WM_NCMOUSEMOVE) MESSAGE_CASE_STR(WM_NCLBUTTONDOWN) MESSAGE_CASE_STR(WM_NCLBUTTONUP) MESSAGE_CASE_STR(WM_NCLBUTTONDBLCLK) MESSAGE_CASE_STR(WM_NCRBUTTONDOWN) MESSAGE_CASE_STR(WM_NCRBUTTONUP) MESSAGE_CASE_STR(WM_NCRBUTTONDBLCLK) MESSAGE_CASE_STR(WM_NCMBUTTONDOWN) MESSAGE_CASE_STR(WM_NCMBUTTONUP) MESSAGE_CASE_STR(WM_NCMBUTTONDBLCLK) MESSAGE_CASE_STR(WM_NCXBUTTONDOWN) MESSAGE_CASE_STR(WM_NCXBUTTONUP) MESSAGE_CASE_STR(WM_NCXBUTTONDBLCLK) MESSAGE_CASE_STR(WM_INPUT_DEVICE_CHANGE) MESSAGE_CASE_STR(WM_INPUT) //MESSAGE_CASE_STR(WM_KEYFIRST) MESSAGE_CASE_STR(WM_KEYDOWN) MESSAGE_CASE_STR(WM_KEYUP) MESSAGE_CASE_STR(WM_CHAR) MESSAGE_CASE_STR(WM_DEADCHAR) MESSAGE_CASE_STR(WM_SYSKEYDOWN) MESSAGE_CASE_STR(WM_SYSKEYUP) MESSAGE_CASE_STR(WM_SYSCHAR) MESSAGE_CASE_STR(WM_SYSDEADCHAR) MESSAGE_CASE_STR(WM_UNICHAR) //MESSAGE_CASE_STR(WM_KEYLAST) MESSAGE_CASE_STR(WM_IME_STARTCOMPOSITION) MESSAGE_CASE_STR(WM_IME_ENDCOMPOSITION) MESSAGE_CASE_STR(WM_IME_COMPOSITION) //MESSAGE_CASE_STR(WM_IME_KEYLAST) MESSAGE_CASE_STR(WM_INITDIALOG) MESSAGE_CASE_STR(WM_COMMAND) MESSAGE_CASE_STR(WM_SYSCOMMAND) MESSAGE_CASE_STR(WM_TIMER) MESSAGE_CASE_STR(WM_HSCROLL) MESSAGE_CASE_STR(WM_VSCROLL) MESSAGE_CASE_STR(WM_INITMENU) MESSAGE_CASE_STR(WM_INITMENUPOPUP) MESSAGE_CASE_STR(WM_GESTURE) MESSAGE_CASE_STR(WM_GESTURENOTIFY) MESSAGE_CASE_STR(WM_MENUSELECT) MESSAGE_CASE_STR(WM_MENUCHAR) MESSAGE_CASE_STR(WM_ENTERIDLE) MESSAGE_CASE_STR(WM_MENURBUTTONUP) MESSAGE_CASE_STR(WM_MENUDRAG) MESSAGE_CASE_STR(WM_MENUGETOBJECT) MESSAGE_CASE_STR(WM_UNINITMENUPOPUP) MESSAGE_CASE_STR(WM_MENUCOMMAND) MESSAGE_CASE_STR(WM_CHANGEUISTATE) MESSAGE_CASE_STR(WM_UPDATEUISTATE) MESSAGE_CASE_STR(WM_QUERYUISTATE) MESSAGE_CASE_STR(WM_CTLCOLORMSGBOX) MESSAGE_CASE_STR(WM_CTLCOLOREDIT) MESSAGE_CASE_STR(WM_CTLCOLORLISTBOX) MESSAGE_CASE_STR(WM_CTLCOLORBTN) MESSAGE_CASE_STR(WM_CTLCOLORDLG) MESSAGE_CASE_STR(WM_CTLCOLORSCROLLBAR) MESSAGE_CASE_STR(WM_CTLCOLORSTATIC) //MESSAGE_CASE_STR(WM_MOUSEFIRST) MESSAGE_CASE_STR(WM_MOUSEMOVE) MESSAGE_CASE_STR(WM_LBUTTONDOWN) MESSAGE_CASE_STR(WM_LBUTTONUP) MESSAGE_CASE_STR(WM_LBUTTONDBLCLK) MESSAGE_CASE_STR(WM_RBUTTONDOWN) MESSAGE_CASE_STR(WM_RBUTTONUP) MESSAGE_CASE_STR(WM_RBUTTONDBLCLK) MESSAGE_CASE_STR(WM_MBUTTONDOWN) MESSAGE_CASE_STR(WM_MBUTTONUP) MESSAGE_CASE_STR(WM_MBUTTONDBLCLK) MESSAGE_CASE_STR(WM_MOUSEWHEEL) MESSAGE_CASE_STR(WM_XBUTTONDOWN) MESSAGE_CASE_STR(WM_XBUTTONUP) MESSAGE_CASE_STR(WM_XBUTTONDBLCLK) MESSAGE_CASE_STR(WM_MOUSEHWHEEL) //MESSAGE_CASE_STR(WM_MOUSELAST) MESSAGE_CASE_STR(WM_PARENTNOTIFY) MESSAGE_CASE_STR(WM_ENTERMENULOOP) MESSAGE_CASE_STR(WM_EXITMENULOOP) MESSAGE_CASE_STR(WM_NEXTMENU) MESSAGE_CASE_STR(WM_SIZING) MESSAGE_CASE_STR(WM_CAPTURECHANGED) MESSAGE_CASE_STR(WM_MOVING) MESSAGE_CASE_STR(WM_POWERBROADCAST) MESSAGE_CASE_STR(WM_DEVICECHANGE) MESSAGE_CASE_STR(WM_MDICREATE) MESSAGE_CASE_STR(WM_MDIDESTROY) MESSAGE_CASE_STR(WM_MDIACTIVATE) MESSAGE_CASE_STR(WM_MDIRESTORE) MESSAGE_CASE_STR(WM_MDINEXT) MESSAGE_CASE_STR(WM_MDIMAXIMIZE) MESSAGE_CASE_STR(WM_MDITILE) MESSAGE_CASE_STR(WM_MDICASCADE) MESSAGE_CASE_STR(WM_MDIICONARRANGE) MESSAGE_CASE_STR(WM_MDIGETACTIVE) MESSAGE_CASE_STR(WM_MDISETMENU) MESSAGE_CASE_STR(WM_ENTERSIZEMOVE) MESSAGE_CASE_STR(WM_EXITSIZEMOVE) MESSAGE_CASE_STR(WM_DROPFILES) MESSAGE_CASE_STR(WM_MDIREFRESHMENU) MESSAGE_CASE_STR(WM_POINTERDEVICECHANGE) MESSAGE_CASE_STR(WM_POINTERDEVICEINRANGE) MESSAGE_CASE_STR(WM_POINTERDEVICEOUTOFRANGE) MESSAGE_CASE_STR(WM_TOUCH) MESSAGE_CASE_STR(WM_NCPOINTERUPDATE) MESSAGE_CASE_STR(WM_NCPOINTERDOWN) MESSAGE_CASE_STR(WM_NCPOINTERUP) MESSAGE_CASE_STR(WM_POINTERUPDATE) MESSAGE_CASE_STR(WM_POINTERDOWN) MESSAGE_CASE_STR(WM_POINTERUP) MESSAGE_CASE_STR(WM_POINTERENTER) MESSAGE_CASE_STR(WM_POINTERLEAVE) MESSAGE_CASE_STR(WM_POINTERACTIVATE) MESSAGE_CASE_STR(WM_POINTERCAPTURECHANGED) MESSAGE_CASE_STR(WM_TOUCHHITTESTING) MESSAGE_CASE_STR(WM_POINTERWHEEL) MESSAGE_CASE_STR(WM_POINTERHWHEEL) MESSAGE_CASE_STR(WM_IME_SETCONTEXT) MESSAGE_CASE_STR(WM_IME_NOTIFY) MESSAGE_CASE_STR(WM_IME_CONTROL) MESSAGE_CASE_STR(WM_IME_COMPOSITIONFULL) MESSAGE_CASE_STR(WM_IME_SELECT) MESSAGE_CASE_STR(WM_IME_CHAR) MESSAGE_CASE_STR(WM_IME_REQUEST) MESSAGE_CASE_STR(WM_IME_KEYDOWN) MESSAGE_CASE_STR(WM_IME_KEYUP) MESSAGE_CASE_STR(WM_MOUSEHOVER) MESSAGE_CASE_STR(WM_MOUSELEAVE) MESSAGE_CASE_STR(WM_NCMOUSEHOVER) MESSAGE_CASE_STR(WM_NCMOUSELEAVE) MESSAGE_CASE_STR(WM_WTSSESSION_CHANGE) MESSAGE_CASE_STR(WM_TABLET_FIRST) MESSAGE_CASE_STR(WM_TABLET_LAST) MESSAGE_CASE_STR(WM_CUT) MESSAGE_CASE_STR(WM_COPY) MESSAGE_CASE_STR(WM_PASTE) MESSAGE_CASE_STR(WM_CLEAR) MESSAGE_CASE_STR(WM_UNDO) MESSAGE_CASE_STR(WM_RENDERFORMAT) MESSAGE_CASE_STR(WM_RENDERALLFORMATS) MESSAGE_CASE_STR(WM_DESTROYCLIPBOARD) MESSAGE_CASE_STR(WM_DRAWCLIPBOARD) MESSAGE_CASE_STR(WM_PAINTCLIPBOARD) MESSAGE_CASE_STR(WM_VSCROLLCLIPBOARD) MESSAGE_CASE_STR(WM_SIZECLIPBOARD) MESSAGE_CASE_STR(WM_ASKCBFORMATNAME) MESSAGE_CASE_STR(WM_CHANGECBCHAIN) MESSAGE_CASE_STR(WM_HSCROLLCLIPBOARD) MESSAGE_CASE_STR(WM_QUERYNEWPALETTE) MESSAGE_CASE_STR(WM_PALETTEISCHANGING) MESSAGE_CASE_STR(WM_PALETTECHANGED) MESSAGE_CASE_STR(WM_HOTKEY) MESSAGE_CASE_STR(WM_PRINT) MESSAGE_CASE_STR(WM_PRINTCLIENT) MESSAGE_CASE_STR(WM_APPCOMMAND) MESSAGE_CASE_STR(WM_THEMECHANGED) MESSAGE_CASE_STR(WM_CLIPBOARDUPDATE) MESSAGE_CASE_STR(WM_DWMCOMPOSITIONCHANGED) MESSAGE_CASE_STR(WM_DWMNCRENDERINGCHANGED) MESSAGE_CASE_STR(WM_DWMCOLORIZATIONCOLORCHANGED) MESSAGE_CASE_STR(WM_DWMWINDOWMAXIMIZEDCHANGE) MESSAGE_CASE_STR(WM_DWMSENDICONICTHUMBNAIL) MESSAGE_CASE_STR(WM_DWMSENDICONICLIVEPREVIEWBITMAP) MESSAGE_CASE_STR(WM_GETTITLEBARINFOEX) MESSAGE_CASE_STR(WM_HANDHELDFIRST) MESSAGE_CASE_STR(WM_HANDHELDLAST) MESSAGE_CASE_STR(WM_AFXFIRST) MESSAGE_CASE_STR(WM_AFXLAST) MESSAGE_CASE_STR(WM_PENWINFIRST) MESSAGE_CASE_STR(WM_PENWINLAST) MESSAGE_CASE_STR(WM_APP) MESSAGE_CASE_STR(WM_USER) default: return "Unknown"; } } #endif #define ASYNC_KEY_IS_DOWN(VK) \ ((GetAsyncKeyState(VK) & 0x8000) != 0) #define MESSAGE_TO_KEY_STATE(Message) \ ((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED) static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam) { LRESULT Result = 0; if (Message == WM_CREATE) { SetWindowLongPtrA(Window, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)LParam)->lpCreateParams); } struct stl_window_win32 *w32 = (struct stl_window_win32 *)GetWindowLongPtrA(Window, GWLP_USERDATA); #ifdef LOG_ALL_WND_MESSAGES al_printf("%s 0x%lx 0x%lx\n", WndMessageString(Message), WParam, LParam); #endif // break = return DefWindowProc(). switch (Message) { case WM_CREATE: case WM_CLOSE: // Special case with Window as WParam. PostThreadMessageW(MainThreadID, Message, (WPARAM)Window, LParam); break; /* case WM_ERASEBKGND: return TRUE; */ case WM_MOUSEACTIVATE: { LRESULT HitTest = LOWORD(LParam); UINT MouseButton = HIWORD(LParam); if (MouseButton == WM_LBUTTONDOWN && HitTest == HTCLIENT) { return MA_ACTIVATEANDEAT; } break; } case WM_LBUTTONDOWN: // https://learn.microsoft.com/en-us/windows/win32/learnwin32/mouse-movement w32->DragCapture = TRUE; if (!GetCapture()) { SetCapture(w32->Window); } goto intercept; case WM_LBUTTONUP: w32->DragCapture = FALSE; ReleaseCapture(); goto intercept; case WM_RBUTTONDOWN: case WM_NCRBUTTONDOWN: #ifdef STELA_MOUSE2_DRAG if (!Global.IsWine) { POINT Pos; Pos.x = GET_X_LPARAM(LParam); Pos.y = GET_Y_LPARAM(LParam); if (Message == WM_RBUTTONDOWN) { ClientToScreen(w32->Window, &Pos); } w32->LastMousePos.x = Pos.x; w32->LastMousePos.y = Pos.y; w32->WindowMoving = TRUE; // DragCapture is a higher priority. if (!w32->DragCapture) { SetCapture(w32->Window); } return Result; } else { /* Message = WM_NCLBUTTONDOWN; WParam = HTCAPTION; */ // https://stackoverflow.com/a/35522584, incredible... // https://gitlab.winehq.org/agusev/wine/-/blob/wine-971221/windows/nonclient.c#L1773 // This works on Wine but not Windows. Everything works the same except on Windows // it seems there is a special case for LBUTTON. SendMessageW(w32->Window, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, LParam); } break; #else goto intercept; #endif case WM_RBUTTONUP: case WM_NCRBUTTONUP: #ifdef STELA_MOUSE2_DRAG if (!Global.IsWine) { w32->WindowMoving = FALSE; if (!w32->DragCapture) { ReleaseCapture(); } return Result; } break; #else goto intercept; #endif case WM_MBUTTONDOWN: case WM_MBUTTONUP: goto intercept; case WM_MOUSELEAVE: goto intercept; case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: if ((WParam & MK_RBUTTON) && w32->WindowMoving) { POINT Pos; Pos.x = GET_X_LPARAM(LParam); Pos.y = GET_Y_LPARAM(LParam); if (Message == WM_MOUSEMOVE) { ClientToScreen(w32->Window, &Pos); } RECT Window; GetWindowRect(w32->Window, &Window); SetWindowPos(w32->Window, NULL, Window.left + (Pos.x - w32->LastMousePos.x), Window.top + (Pos.y - w32->LastMousePos.y), 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); w32->LastMousePos.x = Pos.x; w32->LastMousePos.y = Pos.y; return Result; } goto intercept; case WM_MOUSEWHEEL: goto intercept; case WM_ACTIVATE: // Don't use Wine behavior as a reference, it's too broken. switch (LOWORD(WParam)) { case WA_ACTIVE: case WA_CLICKACTIVE: LParam = STELA_MOD_LCONTROL * ASYNC_KEY_IS_DOWN(VK_LCONTROL) | STELA_MOD_RCONTROL * ASYNC_KEY_IS_DOWN(VK_RCONTROL) | STELA_MOD_LSHIFT * ASYNC_KEY_IS_DOWN(VK_LSHIFT) | STELA_MOD_RSHIFT * ASYNC_KEY_IS_DOWN(VK_RSHIFT) | STELA_MOD_LALT * ASYNC_KEY_IS_DOWN(VK_LMENU) | STELA_MOD_RALT * ASYNC_KEY_IS_DOWN(VK_RMENU); break; case WA_INACTIVE: break; } goto intercept; case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: { // https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input WORD Flags = HIWORD(LParam); bool PreviouslyDown = (Flags & KF_REPEAT) == KF_REPEAT; if (((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) && PreviouslyDown) || ((Message == WM_KEYUP || Message == WM_SYSKEYUP) && !PreviouslyDown)) { break; // Repeat. } WORD Scancode = LOBYTE(Flags); bool Extended = (LParam & KF_EXTENDED) == KF_EXTENDED; if (Extended) Scancode = MAKEWORD(Scancode, 0xE0); switch (WParam) { case VK_SHIFT: case VK_CONTROL: case VK_MENU: WParam = LOWORD(MapVirtualKey(Scancode, MAPVK_VSC_TO_VK_EX)); break; default: break; } stl_window_send_key_immediate_event(&w32->w, MESSAGE_TO_KEY_STATE(Message), Global.keycodes[WParam]); goto intercept; } case WM_SIZE: goto intercept; case SET_CURSOR: //SystemParametersInfo(SPI_SETCURSORS, 0, NULL, 0); // Reset system cursor state. if (WParam == STELA_CURSOR_HIDDEN) { while (ShowCursor(FALSE) >= 0) {} } else { HCURSOR Cursor = Global.Cursors[WParam]; SetClassLongPtr(w32->Window, GCLP_HCURSOR, (LONG_PTR)Cursor); SetCursor(Cursor); ShowCursor(TRUE); } return 0; default: break; } return DefWindowProcW(Window, Message, WParam, LParam); intercept: PostThreadMessageW(MainThreadID, Message, WParam, LParam); return Result; } #ifdef STELA_API_OPENGL static bool CreateWGLContext(struct stl_window_win32 *w32) { #define STELA_INCLUDE_DEPTH_BUFFER PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, #ifdef STELA_INCLUDE_DEPTH_BUFFER PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, #else PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DEPTH_DONTCARE, #endif PFD_TYPE_RGBA, 32, // Colordepth of the framebuffer. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, #ifdef STELA_INCLUDE_DEPTH_BUFFER 24, // Bits for the depthbuffer. 8, // Bits for the stencilbuffer. #else 0, 0, #endif 0, // Number of "Aux" buffers. PFD_MAIN_PLANE, 0, 0, 0, 0 }; w32->DeviceContext = GetDC(w32->Window); s32 letWindowsChooseThisPixelFormat; letWindowsChooseThisPixelFormat = ChoosePixelFormat(w32->DeviceContext, &pfd); SetPixelFormat(w32->DeviceContext, letWindowsChooseThisPixelFormat, &pfd); HGLRC FakeContext = wglCreateContext(w32->DeviceContext); if (!FakeContext) { log_error("Failed to create dummy OpenGL context (0x%lx).", GetLastError()); return false; } wglMakeCurrent(w32->DeviceContext, FakeContext); PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; CreateContextAttribsARB = (void *)wglGetProcAddress("wglCreateContextAttribsARB"); PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; SwapIntervalEXT = (void *)wglGetProcAddress("wglSwapIntervalEXT"); w32->GLContext = NULL; if (CreateContextAttribsARB) { for (u32 i = 0; i < ARRAY_SIZE(stl_gl_vers); i++) { s32 flags = stl_gl_vers[i].major >= 3 ? WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB : 0; s32 mask = 0; if (stl_gl_vers[i].api == STELA_GLES_API) { mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT; } else { mask |= stl_gl_vers[i].profile == STELA_GL_CORE_PROFILE_BIT ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } s32 attribs[] = { WGL_CONTEXT_FLAGS_ARB, flags, WGL_CONTEXT_PROFILE_MASK_ARB, mask, WGL_CONTEXT_MAJOR_VERSION_ARB, stl_gl_vers[i].major, WGL_CONTEXT_MINOR_VERSION_ARB, stl_gl_vers[i].minor, 0, 0 }; STELA_LOG_GL_CONTEXT_CREATION(stl_gl_vers[i]); w32->GLContext = CreateContextAttribsARB(w32->DeviceContext, NULL, attribs); if (w32->GLContext) break; log_warn("Failed to create context (0x%lx).", GetLastError()); } wglDeleteContext(FakeContext); wglMakeCurrent(w32->DeviceContext, w32->GLContext); } else { #ifndef STELA_USE_GLES w32->GLContext = FakeContext; #endif } if (!w32->GLContext) { log_error("Couldn't create an OpenGL context."); return false; } if (SwapIntervalEXT) { SwapIntervalEXT((w32->w.flags & STELA_WINDOW_VSYNC) ? 1 : 0); } wglMakeCurrent(w32->DeviceContext, NULL); return true; } #endif static bool window_win32_create_window(struct stl_window *window, u32 width, u32 height, u32 flags, const char *monitor, const char *name, const char *app_id) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; (void)monitor; (void)app_id; WNDCLASSEXW WindowClass = { 0 }; WindowClass.cbSize = sizeof(WindowClass); WindowClass.lpfnWndProc = &DisplayWndProc; WindowClass.hInstance = Global.ModuleHandle; HANDLE Icon = LoadImageW(Global.ModuleHandle, L"CMV_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); if (!Icon) Icon = LoadIconA(NULL, IDI_APPLICATION); WindowClass.hIcon = Icon; WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW); WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); WindowClass.lpszClassName = DangerousClass; // https://learn.microsoft.com/en-us/windows/win32/winmsg/window-class-styles // On CS_HREDRAW | CS_VREDRAW: The idea is as long as we are equally as notified of // window resizes, let us decide when to redraw. WindowClass.style = 0; RegisterClassExW(&WindowClass); // Add in window decorations so width and height reference the client area. RECT Overlap = { 0, 0, width, height }; AdjustWindowRect(&Overlap, WS_OVERLAPPEDWINDOW, FALSE); struct the_baby Baby = { 0 }; Baby.lpClassName = WindowClass.lpszClassName; al_wstr_from_cstr(&w32->WindowName, (char *)name); Baby.lpWindowName = w32->WindowName.data; // https://learn.microsoft.com/en-us/windows/win32/winmsg/window-styles // https://learn.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles Baby.dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE; Baby.dwExStyle = 0; Baby.X = CW_USEDEFAULT; Baby.Y = CW_USEDEFAULT; Baby.nWidth = Overlap.right - Overlap.left; Baby.nHeight = Overlap.bottom - Overlap.top; Baby.hInstance = WindowClass.hInstance; Baby.lpParam = w32; w32->Window = (HWND)SendMessageW(Global.ServiceWindow, CREATE_DANGEROUS_WINDOW, (WPARAM)&Baby, 0); if (!w32->Window) { log_error("Failed to create window (0x%lx).", GetLastError()); return false; } stl_window_created(&w32->w, width, height, flags); w32->Closed = FALSE; w32->Decorated = TRUE; w32->UserDecorated = TRUE; w32->Prev.Style = GetWindowLong(w32->Window, GWL_STYLE); w32->Prev.ExStyle = GetWindowLong(w32->Window, GWL_EXSTYLE); w32->DragCapture = TRUE; w32->WindowMoving = FALSE; w32->MouseButtonMask = 0; #if defined STELA_API_OPENGL return CreateWGLContext(w32); #elif defined STELA_API_VULKAN return true; #elif defined STELA_API_DX11 w32->w.win32_window = w32->Window; return true; #endif } static void window_win32_resize_internal(struct stl_window *window, u32 width, u32 height) { (void)window; (void)width; (void)height; } static void window_win32_resize(struct stl_window *window, u32 width, u32 height) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; if (!w32->w.fullscreen) { ShowWindow(w32->Window, SW_SHOWNOACTIVATE); RECT Overlap = { 0, 0, width, height }; AdjustWindowRect(&Overlap, WS_OVERLAPPEDWINDOW, FALSE); SetWindowPos(w32->Window, HWND_NOTOPMOST, 0, 0, Overlap.right - Overlap.left, Overlap.bottom - Overlap.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } } static struct stl_monitor_win32 *get_monitor_from_handle(HMONITOR Handle) { struct stl_monitor *monitor; al_array_foreach(Global.monitors, i, monitor) { if (((struct stl_monitor_win32 *)monitor)->Handle == Handle) { return (struct stl_monitor_win32 *)monitor; } } return NULL; } // @TODO: // - Separate decorations and transparent background. // - Fix jump on toggle. static void set_decorations(struct stl_window_win32 *w32, bool enabled) { if (!enabled && w32->Decorated) { // Voodoo... // https://stackoverflow.com/a/2400467 LONG Style = GetWindowLong(w32->Window, GWL_STYLE); LONG ExStyle = GetWindowLong(w32->Window, GWL_EXSTYLE); w32->Prev.Style = Style; w32->Prev.ExStyle = ExStyle; Style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU); //ExStyle |= WS_EX_LAYERED; SetWindowLong(w32->Window, GWL_STYLE, Style); //SetWindowLong(w32->Window, GWL_EXSTYLE, ExStyle); //SetLayeredWindowAttributes(w32->Window, 0, 255, LWA_ALPHA); //SetLayeredWindowAttributes(w32->Window, RGB(0, 0, 0), 0, LWA_COLORKEY); /* SetLayeredWindowAttributes(w32->Window, 0, 0, LWA_ALPHA); BLENDFUNCTION blend = { 0 }; blend.BlendOp = AC_SRC_OVER; blend.SourceConstantAlpha = 0; blend.AlphaFormat = AC_SRC_ALPHA; POINT ptLocation = { 0, 0 }; SIZE szWnd = { w32->w.width, w32->w.height }; POINT ptSrc = { 0, 0 }; UpdateLayeredWindow(w32->Window, NULL, &ptLocation, &szWnd, NULL, &ptSrc, 0, &blend, ULW_ALPHA); */ // https://github.com/mpv-player/mpv/blob/dbd7a905b6ed47dd8f0acd09a1f4cc9a08e854a6/video/out/w32_common.c#L2575C1-L2575C30 #ifndef NAUNET_WIN32_COMPAT_MODE DWM_BLURBEHIND dbb = { 0 }; HRGN Region = CreateRectRgn(0, 0, -1, -1); dbb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; dbb.hRgnBlur = Region; dbb.fEnable = TRUE; DwmEnableBlurBehindWindow(w32->Window, &dbb); DeleteObject(Region); #endif w32->Decorated = FALSE; } else if (enabled && !w32->Decorated) { al_assert(w32->Prev.Style != 0); SetWindowLong(w32->Window, GWL_STYLE, w32->Prev.Style); SetWindowLong(w32->Window, GWL_EXSTYLE, w32->Prev.ExStyle); #ifndef NAUNET_WIN32_COMPAT_MODE DWM_BLURBEHIND dbb = { 0 }; dbb.dwFlags = DWM_BB_ENABLE; dbb.fEnable = FALSE; DwmEnableBlurBehindWindow(w32->Window, &dbb); #endif w32->Decorated = TRUE; } } static void window_win32_toggle_fullscreen(struct stl_window *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; if (!w32->w.fullscreen) { RECT Prev; GetWindowRect(w32->Window, &Prev); // https://learn.microsoft.com/en-us/windows/win32/gdi/positioning-objects-on-a-multiple-display-setup HMONITOR Nearest = MonitorFromRect(&Prev, MONITOR_DEFAULTTONEAREST); struct stl_monitor_win32 *Monitor = get_monitor_from_handle(Nearest); w32->Prev.Monitor = Monitor; w32->Prev.X = Prev.left; w32->Prev.Y = Prev.top; w32->Prev.Width = Prev.right - Prev.left; w32->Prev.Height = Prev.bottom - Prev.top; set_decorations(w32, false); SetWindowPos(w32->Window, HWND_TOPMOST, Monitor->X, Monitor->Y, (s32)Monitor->M.width, (s32)Monitor->M.height, SWP_FRAMECHANGED); } else { struct stl_monitor_win32 *Monitor = w32->Prev.Monitor; s32 X = w32->Prev.X - Monitor->X; s32 Y = w32->Prev.Y - Monitor->Y; if (w32->UserDecorated) { set_decorations(w32, true); } SetWindowPos(w32->Window, HWND_NOTOPMOST, X, Y, w32->Prev.Width, w32->Prev.Height, SWP_FRAMECHANGED); } w32->w.fullscreen = !w32->w.fullscreen; } static void window_win32_set_decorations(struct stl_window *window, bool enabled) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; w32->UserDecorated = enabled; if (!w32->w.fullscreen) { s32 cx, cy, dx = 0, dy = 0; if (!enabled) { RECT Prev; GetWindowRect(w32->Window, &Prev); w32->Prev.X = Prev.left; w32->Prev.Y = Prev.top; w32->Prev.Width = Prev.right - Prev.left; w32->Prev.Height = Prev.bottom - Prev.top; //RECT Over = { 0, 0, w32->w.width, w32->w.height }; //AdjustWindowRect(&Over, WS_OVERLAPPEDWINDOW, FALSE); cx = w32->w.width; cy = w32->w.height; // @TODO: What is 8? dx = ((Prev.right - Prev.left) - cx) - 8; dy = ((Prev.bottom - Prev.top) - cy) - 8; dx = Prev.left + dx; dy = Prev.top + dy; } else { RECT Over = { 0, 0, w32->w.width, w32->w.height }; AdjustWindowRect(&Over, WS_OVERLAPPEDWINDOW, FALSE); cx = w32->Prev.Width; cy = w32->Prev.Height; dx = w32->Prev.X; dy = w32->Prev.Y; } set_decorations(w32, enabled); //UINT Flags = SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE; UINT Flags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOACTIVATE; SetWindowPos(w32->Window, 0, dx, dy, cx, cy, Flags); } } static void SetCursorInternal(struct stl_window_win32 *w32, u8 Shape) { PostMessageW(w32->Window, SET_CURSOR, Shape, 0); } static void window_win32_set_cursor(struct stl_window *window, u8 shape) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; SetCursorInternal(w32, shape); } static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message) { switch (Message->message) { case WM_CREATE: break; case WM_CLOSE: w32->Closed = TRUE; #ifdef STELA_API_OPENGL wglDeleteContext(w32->GLContext); // Makes context not current. #endif stl_window_send_should_close_event(&w32->w); break; case WM_LBUTTONDOWN: { /* TRACKMOUSEEVENT TrackMouse = { 0 }; TrackMouse.cbSize = sizeof(TRACKMOUSEEVENT); TrackMouse.dwFlags = TME_LEAVE; TrackMouse.hwndTrack = w32->Window; TrackMouse.dwHoverTime = HOVER_DEFAULT; TrackMouseEvent(&TrackMouse); */ w32->MouseButtonMask |= STELA_MOUSE1; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE1); break; } case WM_LBUTTONUP: { /* TRACKMOUSEEVENT TrackMouse = { 0 }; TrackMouse.cbSize = sizeof(TRACKMOUSEEVENT); TrackMouse.dwFlags = TME_CANCEL | TME_LEAVE; TrackMouse.hwndTrack = w32->Window; TrackMouse.dwHoverTime = HOVER_DEFAULT; TrackMouseEvent(&TrackMouse); */ w32->MouseButtonMask &= ~STELA_MOUSE1; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE1); break; } case WM_RBUTTONDOWN: w32->MouseButtonMask |= STELA_MOUSE2; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE2); break; case WM_RBUTTONUP: w32->MouseButtonMask &= ~STELA_MOUSE2; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE2); break; case WM_MBUTTONDOWN: w32->MouseButtonMask |= STELA_MOUSE3; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE3); break; case WM_MBUTTONUP: w32->MouseButtonMask &= ~STELA_MOUSE3; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE3); break; case WM_MOUSELEAVE: /* if (w32->MouseButtonMask & STELA_MOUSE1) { w32->MouseButtonMask &= ~STELA_MOUSE1; stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE1); } */ break; case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: { POINT Pos; Pos.x = GET_X_LPARAM(Message->lParam); Pos.y = GET_Y_LPARAM(Message->lParam); if (Message->message == WM_NCMOUSEMOVE) { ScreenToClient(w32->Window, &Pos); } stl_window_send_pointer_pos_event(&w32->w, (f64)Pos.x, (f64)Pos.y); break; } case WM_MOUSEWHEEL: { SHORT Value = (SHORT)HIWORD(Message->wParam); f64 Bits = 0.0; al_memcpy(&Bits, &Value, sizeof(SHORT)); stl_window_send_scroll_event(&w32->w, Bits); break; } case WM_ACTIVATE: switch (LOWORD(Message->wParam)) { case WA_ACTIVE: case WA_CLICKACTIVE: { u32 *mods = stl_all_modifiers; u32 mod; while ((mod = *mods++) != STELA_MOD_NONE) { if (Message->lParam & mod) { stl_window_send_key_event(&w32->w, STELA_BUTTON_PRESSED, stl_mod_to_key(mod)); } } break; } case WA_INACTIVE: stl_window_drop_modifiers(&w32->w); break; } break; case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: stl_window_send_key_event(&w32->w, MESSAGE_TO_KEY_STATE(Message->message), Global.keycodes[Message->wParam]); break; case WM_SIZE: // Gives the size of the client area. w32->w.width = LOWORD(Message->lParam); w32->w.height = HIWORD(Message->lParam); stl_window_send_resize_event(&w32->w, w32->w.width, w32->w.height); break; case WAKE_WINDOW: #ifdef STELA_PAUSE w32->w.pending_refresh = true; #endif break; default: break; } } #ifdef STELA_POLL_INLINE #ifdef STELA_EVENT_BUFFER #error "window_win32_poll() is incompatible with event buffer mode." #endif static bool window_win32_poll(struct stl_window *window, bool block) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; if (block) { MSG Message; GetMessageW(&Message, 0, 0, 0); win32_handle_message(w32, &Message); } return true; } #ifdef STELA_PAUSE static void window_win32_wake(struct stl_window *window) { (void)window; PostThreadMessageW(MainThreadID, WAKE_WINDOW, 0, 0); } #endif #endif static void window_win32_process_events(struct stl_window *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; MSG Message; while (PeekMessage(&Message, 0, 0, 0, PM_REMOVE)) { win32_handle_message(w32, &Message); } } #ifdef STELA_PAUSE static bool window_win32_should_refresh(struct stl_window *window) { return stl_should_refresh_common(window); } #endif static void window_win32_free(struct stl_window **window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)*window; SendMessageW(Global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32, 0); al_wstr_free(&w32->WindowName); al_free(w32); *window = NULL; } #if defined STELA_API_VULKAN static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_win32_get_vk_proc_address(VkInstance inst, const char *name) { return stl_get_vk_proc_address(inst, name); } static VkResult window_win32_vk_create_surface(void *window, VkInstance inst, VkSurfaceKHR *surface) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR; CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)stl_get_vk_proc_address(inst, "vkCreateWin32SurfaceKHR"); if (!CreateWin32SurfaceKHR) { return VK_ERROR_EXTENSION_NOT_PRESENT; } VkWin32SurfaceCreateInfoKHR info = { .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, .hinstance = Global.ModuleHandle, .hwnd = w32->Window }; VkResult ret = CreateWin32SurfaceKHR(inst, &info, NULL, surface); if (ret != VK_SUCCESS) { log_error("Failed to create Win32 Vulkan surface."); } return ret; } static VkResult window_win32_vk_destroy_surface(VkInstance inst, VkSurfaceKHR surface) { return stl_vk_destroy_surface(inst, surface); } static const char *const *window_win32_vk_get_extensions(u32 *num) { *num = 2; return Global.VKExtensions; } #elif defined STELA_API_OPENGL static bool window_win32_gl_loader_load(void *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; return stl_gl_loader_load(w32->w.get_gl_proc_address); } static bool window_win32_gl_make_current(void *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; if (w32->GLContext != w32->CurrentContext) { w32->CurrentContext = w32->GLContext; return wglMakeCurrent(w32->DeviceContext, w32->GLContext); } return true; } static void window_win32_gl_release_current(void *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; al_assert(w32->GLContext == w32->CurrentContext); #if 1 w32->CurrentContext = NULL; wglMakeCurrent(w32->DeviceContext, NULL); #endif } static void window_win32_gl_swap_buffers(void *window) { struct stl_window_win32 *w32 = (struct stl_window_win32 *)window; SwapBuffers(w32->DeviceContext); } #endif struct stl_window *stl_window_create(void) { log_info("Creating Win32 window."); struct stl_window_win32 *w32 = al_alloc_object(struct stl_window_win32); stl_window_init(&w32->w); w32->w.fullscreen = false; w32->w.create_window = window_win32_create_window; w32->w.resize_internal = window_win32_resize_internal; w32->w.resize = window_win32_resize; w32->w.toggle_fullscreen = window_win32_toggle_fullscreen; w32->w.set_decorations = window_win32_set_decorations; w32->w.set_cursor = window_win32_set_cursor; #ifdef STELA_POLL_INLINE w32->w.poll = window_win32_poll; #ifdef STELA_PAUSE w32->w.wake = window_win32_wake; #endif #endif w32->w.process_events = window_win32_process_events; #ifdef STELA_PAUSE w32->w.should_refresh = window_win32_should_refresh; #endif w32->w.free = window_win32_free; #if defined STELA_API_VULKAN w32->w.get_vk_proc_address = window_win32_get_vk_proc_address; w32->w.vk_create_surface = window_win32_vk_create_surface; w32->w.vk_destroy_surface = window_win32_vk_destroy_surface; w32->w.vk_get_extensions = window_win32_vk_get_extensions; #elif defined STELA_API_OPENGL w32->w.get_gl_proc_address = (void (*(*)(const char *))(void))GetAnyGLFuncAddress; w32->w.gl_loader_load = window_win32_gl_loader_load; w32->w.gl_make_current = window_win32_gl_make_current; w32->w.gl_release_current = window_win32_gl_release_current; w32->w.gl_swap_buffers = window_win32_gl_swap_buffers; #endif return (struct stl_window *)w32; }