Work on getting rid of the inner hWnd. Currently hangs on exit.

This commit is contained in:
Henrik Rydgård 2025-02-25 12:19:38 -06:00
parent 805816da1d
commit 776858f271
4 changed files with 172 additions and 224 deletions

View file

@ -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 {

View file

@ -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<POINT*>(&rc.left));
ClientToScreen(hwndDisplay, reinterpret_cast<POINT*>(&rc.right));
GetClientRect(hwndMain, &rc);
ClientToScreen(hwndMain, reinterpret_cast<POINT*>(&rc.left));
ClientToScreen(hwndMain, reinterpret_cast<POINT*>(&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() {

View file

@ -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

View file

@ -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: