From 776858f2710ab2bfda97a7b6254e290ed133c447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 25 Feb 2025 12:19:38 -0600 Subject: [PATCH] Work on getting rid of the inner hWnd. Currently hangs on exit. --- Windows/EmuThread.cpp | 2 +- Windows/MainWindow.cpp | 389 ++++++++++++++++++----------------------- Windows/MainWindow.h | 1 - Windows/main.cpp | 4 +- 4 files changed, 172 insertions(+), 224 deletions(-) diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 4b84983af5..791d45f5e4 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -186,7 +186,7 @@ bool CreateGraphicsBackend(std::string *error_message, GraphicsContext **ctx) { return false; } - if (graphicsContext->Init(MainWindow::GetHInstance(), MainWindow::GetDisplayHWND(), error_message)) { + if (graphicsContext->Init(MainWindow::GetHInstance(), MainWindow::GetHWND(), error_message)) { *ctx = graphicsContext; return true; } else { diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index 82ebcb33f0..166ec267c4 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -123,7 +123,6 @@ static std::wstring windowTitle; namespace MainWindow { HWND hwndMain; - HWND hwndDisplay; HWND hwndGameList; TouchInputHandler touchHandler; static HMENU menu; @@ -152,20 +151,14 @@ namespace MainWindow #define MAX_LOADSTRING 100 const TCHAR *szWindowClass = TEXT("PPSSPPWnd"); - const TCHAR *szDisplayClass = TEXT("PPSSPPDisplay"); // Forward declarations of functions included in this code module: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); - LRESULT CALLBACK DisplayProc(HWND, UINT, WPARAM, LPARAM); HWND GetHWND() { return hwndMain; } - HWND GetDisplayHWND() { - return hwndDisplay; - } - void SetKeepScreenBright(bool keepBright) { g_keepScreenBright = keepBright; } @@ -185,21 +178,6 @@ namespace MainWindow wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_PPSSPP); wcex.hIconSm = (HICON)LoadImage(hInstance, (LPCTSTR)IDI_PPSSPP, IMAGE_ICON, 16, 16, LR_SHARED); RegisterClassEx(&wcex); - - WNDCLASSEX wcdisp; - memset(&wcdisp, 0, sizeof(wcdisp)); - // Display Window (contained in main window) - wcdisp.cbSize = sizeof(WNDCLASSEX); - wcdisp.style = CS_HREDRAW | CS_VREDRAW; - wcdisp.lpfnWndProc = (WNDPROC)DisplayProc; - wcdisp.hInstance = hInstance; - wcdisp.hCursor = LoadCursor(NULL, IDC_ARROW); - wcdisp.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - wcdisp.lpszMenuName = 0; - wcdisp.lpszClassName = szDisplayClass; - wcdisp.hIcon = 0; - wcdisp.hIconSm = 0; - RegisterClassEx(&wcdisp); } void SavePosition() { @@ -260,9 +238,9 @@ namespace MainWindow } if (g_Config.bMouseConfine) { RECT rc; - GetClientRect(hwndDisplay, &rc); - ClientToScreen(hwndDisplay, reinterpret_cast(&rc.left)); - ClientToScreen(hwndDisplay, reinterpret_cast(&rc.right)); + GetClientRect(hwndMain, &rc); + ClientToScreen(hwndMain, reinterpret_cast(&rc.left)); + ClientToScreen(hwndMain, reinterpret_cast(&rc.right)); ClipCursor(&rc); } } else { @@ -285,9 +263,6 @@ namespace MainWindow int width, height; W32Util::GetWindowRes(hwndMain, &width, &height); - // Moves the internal display window to match the inner size of the main window. - MoveWindow(hwndDisplay, 0, 0, width, height, TRUE); - // Setting pixelWidth to be too small could have odd consequences. if (width >= 4 && height >= 4) { // The framebuffer manager reads these once per frame, hopefully safe enough.. should really use a mutex or some @@ -497,14 +472,6 @@ namespace MainWindow const DWM_WINDOW_CORNER_PREFERENCE pref = DWMWCP_DONOTROUND; DwmSetWindowAttribute(hwndMain, DWMWA_WINDOW_CORNER_PREFERENCE, &pref, sizeof(pref)); - RECT rcClient; - GetClientRect(hwndMain, &rcClient); - - hwndDisplay = CreateWindowEx(0, szDisplayClass, L"", WS_CHILD | WS_VISIBLE, - 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hwndMain, 0, hInstance, 0); - if (!hwndDisplay) - return FALSE; - menu = GetMenu(hwndMain); MENUINFO info; @@ -531,7 +498,7 @@ namespace MainWindow W32Util::MakeTopMost(hwndMain, g_Config.bTopMost); - touchHandler.registerTouchWindow(hwndDisplay); + touchHandler.registerTouchWindow(hwndMain); WindowsRawInput::Init(); @@ -605,186 +572,6 @@ namespace MainWindow vfpudlg = nullptr; } - LRESULT CALLBACK DisplayProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - static bool firstErase = true; - - switch (message) { - case WM_SIZE: - break; - - case WM_SETFOCUS: - break; - - case WM_SETCURSOR: - if ((lParam & 0xFFFF) == HTCLIENT && g_Config.bShowImDebugger) { - LPTSTR win32_cursor = 0; - if (g_Config.bShowImDebugger) { - switch (ImGui_ImplPlatform_GetCursor()) { - case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; - case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; - case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; - case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; - case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; - case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; - case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; - case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; - case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; - } - } - if (win32_cursor) { - SetCursor(::LoadCursor(nullptr, win32_cursor)); - } else { - SetCursor(nullptr); - } - return TRUE; - } else { - return DefWindowProc(hWnd, message, wParam, lParam); - } - break; - - case WM_ERASEBKGND: - if (firstErase) { - firstErase = false; - // Paint black on first erase while OpenGL stuff is loading - return DefWindowProc(hWnd, message, wParam, lParam); - } - // Then never erase, let the OpenGL drawing take care of everything. - return 1; - - // Mouse input. We send asynchronous touch events for minimal latency. - case WM_LBUTTONDOWN: - if (!touchHandler.hasTouch() || - (GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN) - { - // Hack: Take the opportunity to show the cursor. - mouseButtonDown = true; - - float x = GET_X_LPARAM(lParam) * g_display.dpi_scale; - float y = GET_Y_LPARAM(lParam) * g_display.dpi_scale; - WindowsRawInput::SetMousePos(x, y); - - TouchInput touch{}; - touch.flags = TOUCH_DOWN | TOUCH_MOUSE; - touch.buttons = 1; - touch.x = x; - touch.y = y; - NativeTouch(touch); - SetCapture(hWnd); - - // Simulate doubleclick, doesn't work with RawInput enabled - static double lastMouseDownTime; - static float lastMouseDownX = -1.0f; - static float lastMouseDownY = -1.0f; - const double now = time_now_d(); - if ((now - lastMouseDownTime) < 0.001 * GetDoubleClickTime()) { - const float dx = lastMouseDownX - x; - const float dy = lastMouseDownY - y; - const float distSq = dx * dx + dy * dy; - if (distSq < 3.0f*3.0f && !g_Config.bShowTouchControls && !g_Config.bShowImDebugger && !g_Config.bMouseControl && GetUIState() == UISTATE_INGAME && g_Config.bFullscreenOnDoubleclick) { - SendToggleFullscreen(!g_Config.UseFullScreen()); - } - lastMouseDownTime = 0.0; - } else { - lastMouseDownTime = now; - } - lastMouseDownX = x; - lastMouseDownY = y; - } - break; - - case WM_MOUSEMOVE: - if (!touchHandler.hasTouch() || - (GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN) - { - // Hack: Take the opportunity to show the cursor. - mouseButtonDown = (wParam & MK_LBUTTON) != 0; - int cursorX = GET_X_LPARAM(lParam); - int cursorY = GET_Y_LPARAM(lParam); - if (abs(cursorX - prevCursorX) > 1 || abs(cursorY - prevCursorY) > 1) { - hideCursor = false; - SetTimer(hwndMain, TIMER_CURSORMOVEUPDATE, CURSORUPDATE_MOVE_TIMESPAN_MS, 0); - } - prevCursorX = cursorX; - prevCursorY = cursorY; - - float x = (float)cursorX * g_display.dpi_scale; - float y = (float)cursorY * g_display.dpi_scale; - WindowsRawInput::SetMousePos(x, y); - - // Mouse moves now happen also when no button is pressed. - TouchInput touch{}; - touch.flags = TOUCH_MOVE | TOUCH_MOUSE; - if (wParam & MK_LBUTTON) { - touch.buttons |= 1; - } - if (wParam & MK_RBUTTON) { - touch.buttons |= 2; - } - touch.x = x; - touch.y = y; - NativeTouch(touch); - } - break; - - case WM_LBUTTONUP: - if (!touchHandler.hasTouch() || - (GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN) - { - // Hack: Take the opportunity to hide the cursor. - mouseButtonDown = false; - - float x = (float)GET_X_LPARAM(lParam) * g_display.dpi_scale; - float y = (float)GET_Y_LPARAM(lParam) * g_display.dpi_scale; - WindowsRawInput::SetMousePos(x, y); - - TouchInput touch{}; - touch.buttons = 1; - touch.flags = TOUCH_UP | TOUCH_MOUSE; - touch.x = x; - touch.y = y; - NativeTouch(touch); - ReleaseCapture(); - } - break; - - case WM_TOUCH: - touchHandler.handleTouchEvent(hWnd, message, wParam, lParam); - return 0; - - case WM_RBUTTONDOWN: - { - float x = GET_X_LPARAM(lParam) * g_display.dpi_scale; - float y = GET_Y_LPARAM(lParam) * g_display.dpi_scale; - - TouchInput touch{}; - touch.buttons = 2; - touch.flags = TOUCH_DOWN | TOUCH_MOUSE; - touch.x = x; - touch.y = y; - NativeTouch(touch); - break; - } - - case WM_RBUTTONUP: - { - float x = GET_X_LPARAM(lParam) * g_display.dpi_scale; - float y = GET_Y_LPARAM(lParam) * g_display.dpi_scale; - - TouchInput touch{}; - touch.buttons = 2; - touch.flags = TOUCH_UP | TOUCH_MOUSE; - touch.x = x; - touch.y = y; - NativeTouch(touch); - break; - } - - default: - return DefWindowProc(hWnd, message, wParam, lParam); - } - return 0; - } - RECT MapRectFromClientToWndCoords(HWND hwnd, const RECT & r) { RECT wnd_coords = r; @@ -838,6 +625,7 @@ namespace MainWindow if (UAHDarkModeWndProc(hWnd, message, wParam, lParam, &darkResult)) { return darkResult; } + static int firstErases = 3; switch (message) { case WM_CREATE: @@ -857,6 +645,7 @@ namespace MainWindow callback(hWnd, userdata); break; } + case WM_USER_GET_BASE_POINTER: Reporting::NotifyDebugger(); switch (lParam) { @@ -954,8 +743,13 @@ namespace MainWindow break; case WM_ERASEBKGND: - // This window is always covered by DisplayWindow. No reason to erase. - return 0; + if (firstErases > 0) { + firstErases--; + // Paint black on first erase while GPU stuff is loading + return DefWindowProc(hWnd, message, wParam, lParam); + } + // Then never erase, let the GPU drawing take care of everything. + return 1; case WM_MOVE: SavePosition(); @@ -1221,6 +1015,161 @@ namespace MainWindow return DefWindowProc(hWnd, message, wParam, lParam); } + case WM_SETCURSOR: + if ((lParam & 0xFFFF) == HTCLIENT && g_Config.bShowImDebugger) { + LPTSTR win32_cursor = 0; + if (g_Config.bShowImDebugger) { + switch (ImGui_ImplPlatform_GetCursor()) { + case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; + case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; + case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; + case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; + case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; + case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; + case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; + case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break; + case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break; + } + } + if (win32_cursor) { + SetCursor(::LoadCursor(nullptr, win32_cursor)); + } else { + SetCursor(nullptr); + } + return TRUE; + } else { + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + // Mouse input. We send asynchronous touch events for minimal latency. + case WM_LBUTTONDOWN: + if (!touchHandler.hasTouch() || + (GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN) + { + // Hack: Take the opportunity to show the cursor. + mouseButtonDown = true; + + float x = GET_X_LPARAM(lParam) * g_display.dpi_scale; + float y = GET_Y_LPARAM(lParam) * g_display.dpi_scale; + WindowsRawInput::SetMousePos(x, y); + + TouchInput touch{}; + touch.flags = TOUCH_DOWN | TOUCH_MOUSE; + touch.buttons = 1; + touch.x = x; + touch.y = y; + NativeTouch(touch); + SetCapture(hWnd); + + // Simulate doubleclick, doesn't work with RawInput enabled + static double lastMouseDownTime; + static float lastMouseDownX = -1.0f; + static float lastMouseDownY = -1.0f; + const double now = time_now_d(); + if ((now - lastMouseDownTime) < 0.001 * GetDoubleClickTime()) { + const float dx = lastMouseDownX - x; + const float dy = lastMouseDownY - y; + const float distSq = dx * dx + dy * dy; + if (distSq < 3.0f * 3.0f && !g_Config.bShowTouchControls && !g_Config.bShowImDebugger && !g_Config.bMouseControl && GetUIState() == UISTATE_INGAME && g_Config.bFullscreenOnDoubleclick) { + SendToggleFullscreen(!g_Config.UseFullScreen()); + } + lastMouseDownTime = 0.0; + } else { + lastMouseDownTime = now; + } + lastMouseDownX = x; + lastMouseDownY = y; + } + break; + + case WM_MOUSEMOVE: + if (!touchHandler.hasTouch() || + (GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN) + { + // Hack: Take the opportunity to show the cursor. + mouseButtonDown = (wParam & MK_LBUTTON) != 0; + int cursorX = GET_X_LPARAM(lParam); + int cursorY = GET_Y_LPARAM(lParam); + if (abs(cursorX - prevCursorX) > 1 || abs(cursorY - prevCursorY) > 1) { + hideCursor = false; + SetTimer(hwndMain, TIMER_CURSORMOVEUPDATE, CURSORUPDATE_MOVE_TIMESPAN_MS, 0); + } + prevCursorX = cursorX; + prevCursorY = cursorY; + + float x = (float)cursorX * g_display.dpi_scale; + float y = (float)cursorY * g_display.dpi_scale; + WindowsRawInput::SetMousePos(x, y); + + // Mouse moves now happen also when no button is pressed. + TouchInput touch{}; + touch.flags = TOUCH_MOVE | TOUCH_MOUSE; + if (wParam & MK_LBUTTON) { + touch.buttons |= 1; + } + if (wParam & MK_RBUTTON) { + touch.buttons |= 2; + } + touch.x = x; + touch.y = y; + NativeTouch(touch); + } + break; + + case WM_LBUTTONUP: + if (!touchHandler.hasTouch() || + (GetMessageExtraInfo() & MOUSEEVENTF_MASK_PLUS_PENTOUCH) != MOUSEEVENTF_FROMTOUCH_NOPEN) + { + // Hack: Take the opportunity to hide the cursor. + mouseButtonDown = false; + + float x = (float)GET_X_LPARAM(lParam) * g_display.dpi_scale; + float y = (float)GET_Y_LPARAM(lParam) * g_display.dpi_scale; + WindowsRawInput::SetMousePos(x, y); + + TouchInput touch{}; + touch.buttons = 1; + touch.flags = TOUCH_UP | TOUCH_MOUSE; + touch.x = x; + touch.y = y; + NativeTouch(touch); + ReleaseCapture(); + } + break; + + case WM_TOUCH: + touchHandler.handleTouchEvent(hWnd, message, wParam, lParam); + return 0; + + case WM_RBUTTONDOWN: + { + float x = GET_X_LPARAM(lParam) * g_display.dpi_scale; + float y = GET_Y_LPARAM(lParam) * g_display.dpi_scale; + + TouchInput touch{}; + touch.buttons = 2; + touch.flags = TOUCH_DOWN | TOUCH_MOUSE; + touch.x = x; + touch.y = y; + NativeTouch(touch); + break; + } + + case WM_RBUTTONUP: + { + float x = GET_X_LPARAM(lParam) * g_display.dpi_scale; + float y = GET_Y_LPARAM(lParam) * g_display.dpi_scale; + + TouchInput touch{}; + touch.buttons = 2; + touch.flags = TOUCH_UP | TOUCH_MOUSE; + touch.x = x; + touch.y = y; + NativeTouch(touch); + break; + } + default: return DefWindowProc(hWnd, message, wParam, lParam); } @@ -1228,7 +1177,7 @@ namespace MainWindow } void Redraw() { - InvalidateRect(hwndDisplay,0,0); + InvalidateRect(hwndMain, nullptr, FALSE); } HINSTANCE GetHInstance() { diff --git a/Windows/MainWindow.h b/Windows/MainWindow.h index f27a5c2ec6..166b60384e 100644 --- a/Windows/MainWindow.h +++ b/Windows/MainWindow.h @@ -73,7 +73,6 @@ namespace MainWindow void Redraw(); HWND GetHWND(); HINSTANCE GetHInstance(); - HWND GetDisplayHWND(); void ToggleFullscreen(HWND hWnd, bool goingFullscreen); void Minimize(); void SendToggleFullscreen(bool fullscreen); // To be used off-thread diff --git a/Windows/main.cpp b/Windows/main.cpp index 8512ed5c38..907917a92f 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -215,7 +215,7 @@ std::string System_GetProperty(SystemProperty prop) { case SYSPROP_CLIPBOARD_TEXT: { std::string retval; - if (OpenClipboard(MainWindow::GetDisplayHWND())) { + if (OpenClipboard(MainWindow::GetHWND())) { HANDLE handle = GetClipboardData(CF_UNICODETEXT); const wchar_t *wstr = (const wchar_t*)GlobalLock(handle); if (wstr) @@ -567,7 +567,7 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string case SystemRequestType::COPY_TO_CLIPBOARD: { std::wstring data = ConvertUTF8ToWString(param1); - W32Util::CopyTextToClipboard(MainWindow::GetDisplayHWND(), data); + W32Util::CopyTextToClipboard(MainWindow::GetHWND(), data); return true; } case SystemRequestType::SET_WINDOW_TITLE: