LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: break; case WM_GETMINMAXINFO: { MINMAXINFO *minmax = reinterpret_cast<MINMAXINFO *>(lParam); RECT rc = { 0 }; bool portrait = g_Config.IsPortrait(); rc.right = portrait ? 272 : 480; rc.bottom = portrait ? 480 : 272; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, TRUE); minmax->ptMinTrackSize.x = rc.right - rc.left; minmax->ptMinTrackSize.y = rc.bottom - rc.top; } return 0; case WM_ACTIVATE: { bool pause = true; if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) { WindowsRawInput::GainFocus(); InputDevice::GainFocus(); g_activeWindow = WINDOW_MAINWINDOW; pause = false; } if (!noFocusPause && g_Config.bPauseOnLostFocus && GetUIState() == UISTATE_INGAME) { if (pause != Core_IsStepping()) { // != is xor for bools if (disasmWindow[0]) SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else Core_EnableStepping(pause); } } if (wParam == WA_ACTIVE) { NativeMessageReceived("got_focus", ""); } if (wParam == WA_INACTIVE) { NativeMessageReceived("lost_focus", ""); WindowsRawInput::LoseFocus(); InputDevice::LoseFocus(); } } break; case WM_ERASEBKGND: // This window is always covered by DisplayWindow. No reason to erase. return 1; case WM_MOVE: SavePosition(); break; case WM_SIZE: switch (wParam) { case SIZE_RESTORED: case SIZE_MAXIMIZED: if (g_IgnoreWM_SIZE) { return DefWindowProc(hWnd, message, wParam, lParam); } else { SavePosition(); Core_NotifyWindowHidden(false); if (!g_Config.bPauseWhenMinimized) { NativeMessageReceived("window minimized", "false"); } int width = 0, height = 0; RECT rc; GetClientRect(hwndMain, &rc); width = rc.right - rc.left; height = rc.bottom - rc.top; // 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 // much better mechanism. PSP_CoreParameter().pixelWidth = width; PSP_CoreParameter().pixelHeight = height; } UpdateRenderResolution(); if (UpdateScreenScale(width, height, IsWindowSmall())) { NativeMessageReceived("gpu resized", ""); } // Don't save the window state if fullscreen. if (!g_Config.bFullScreen) { g_WindowState = wParam; } } break; case SIZE_MINIMIZED: Core_NotifyWindowHidden(true); if (!g_Config.bPauseWhenMinimized) { NativeMessageReceived("window minimized", "true"); } break; default: break; } break; case WM_TIMER: // Hack: Take the opportunity to also show/hide the mouse cursor in fullscreen mode. switch (wParam) { case TIMER_CURSORUPDATE: CorrectCursor(); return 0; case TIMER_CURSORMOVEUPDATE: hideCursor = true; KillTimer(hWnd, TIMER_CURSORMOVEUPDATE); return 0; } break; // For some reason, need to catch this here rather than in DisplayProc. case WM_MOUSEWHEEL: { int wheelDelta = (short)(wParam >> 16); KeyInput key; key.deviceId = DEVICE_ID_MOUSE; if (wheelDelta < 0) { key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN; wheelDelta = -wheelDelta; } else { key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP; } // There's no separate keyup event for mousewheel events, let's pass them both together. // This also means it really won't work great for key mapping :( Need to build a 1 frame delay or something. key.flags = KEY_DOWN | KEY_UP | KEY_HASWHEELDELTA | (wheelDelta << 16); NativeKey(key); } break; case WM_COMMAND: { if (!EmuThread_Ready()) return DefWindowProc(hWnd, message, wParam, lParam); MainWindowMenu_Process(hWnd, wParam); } break; case WM_USER_TOGGLE_FULLSCREEN: ToggleFullscreen(hwndMain, !g_Config.bFullScreen); break; case WM_INPUT: return WindowsRawInput::Process(hWnd, wParam, lParam); // TODO: Could do something useful with WM_INPUT_DEVICE_CHANGE? // Not sure why we are actually getting WM_CHAR even though we use RawInput, but alright.. case WM_CHAR: return WindowsRawInput::ProcessChar(hWnd, wParam, lParam); case WM_VERYSLEEPY_MSG: switch (wParam) { case VERYSLEEPY_WPARAM_SUPPORTED: return TRUE; case VERYSLEEPY_WPARAM_GETADDRINFO: { VerySleepy_AddrInfo *info = (VerySleepy_AddrInfo *)lParam; const u8 *ptr = (const u8 *)info->addr; std::string name; if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr(ptr, name)) { swprintf_s(info->name, L"Jit::%S", name.c_str()); return TRUE; } if (gpu && gpu->DescribeCodePtr(ptr, name)) { swprintf_s(info->name, L"GPU::%S", name.c_str()); return TRUE; } } return FALSE; default: return FALSE; } break; case WM_DROPFILES: { if (!EmuThread_Ready()) return DefWindowProc(hWnd, message, wParam, lParam); HDROP hdrop = (HDROP)wParam; int count = DragQueryFile(hdrop,0xFFFFFFFF,0,0); if (count != 1) { MessageBox(hwndMain,L"You can only load one file at a time",L"Error",MB_ICONINFORMATION); } else { TCHAR filename[512]; DragQueryFile(hdrop,0,filename,512); TCHAR *type = filename+_tcslen(filename)-3; NativeMessageReceived("boot", ConvertWStringToUTF8(filename).c_str()); Core_EnableStepping(false); } } break; case WM_CLOSE: EmuThread_Stop(); InputDevice::StopPolling(); WindowsRawInput::Shutdown(); return DefWindowProc(hWnd,message,wParam,lParam); case WM_DESTROY: KillTimer(hWnd, TIMER_CURSORUPDATE); KillTimer(hWnd, TIMER_CURSORMOVEUPDATE); PostQuitMessage(0); break; case WM_USER + 1: if (disasmWindow[0]) disasmWindow[0]->NotifyMapLoaded(); if (memoryWindow[0]) memoryWindow[0]->NotifyMapLoaded(); if (disasmWindow[0]) disasmWindow[0]->UpdateDialog(); SetForegroundWindow(hwndMain); break; case WM_USER_SAVESTATE_FINISH: SetCursor(LoadCursor(0, IDC_ARROW)); break; case WM_USER_UPDATE_UI: TranslateMenus(hwndMain, menu); break; case WM_USER_UPDATE_SCREEN: ShowScreenResolution(); break; case WM_USER_WINDOW_TITLE_CHANGED: UpdateWindowTitle(); break; case WM_USER_BROWSE_BOOT_DONE: BrowseAndBootDone(); break; case WM_MENUSELECT: // Unfortunately, accelerate keys (hotkeys) shares the same enabled/disabled states // with corresponding menu items. UpdateMenus(); WindowsRawInput::NotifyMenu(); break; // Turn off the screensaver. // Note that if there's a screensaver password, this simple method // doesn't work on Vista or higher. case WM_SYSCOMMAND: { switch (wParam) { case SC_SCREENSAVE: return 0; case SC_MONITORPOWER: return 0; } return DefWindowProc(hWnd, message, wParam, lParam); } default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: if (!DoesVersionMatchWindows(6, 0, 0, 0, true)) { // Remove the D3D11 choice on versions below XP RemoveMenu(GetMenu(hWnd), ID_OPTIONS_DIRECT3D11, MF_BYCOMMAND); } break; case WM_GETMINMAXINFO: { MINMAXINFO *minmax = reinterpret_cast<MINMAXINFO *>(lParam); RECT rc = { 0 }; bool portrait = g_Config.IsPortrait(); rc.right = portrait ? 272 : 480; rc.bottom = portrait ? 480 : 272; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, TRUE); minmax->ptMinTrackSize.x = rc.right - rc.left; minmax->ptMinTrackSize.y = rc.bottom - rc.top; } return 0; case WM_ACTIVATE: { bool pause = true; if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) { WindowsRawInput::GainFocus(); if (!IsIconic(GetHWND())) { InputDevice::GainFocus(); } g_activeWindow = WINDOW_MAINWINDOW; pause = false; } if (!noFocusPause && g_Config.bPauseOnLostFocus && GetUIState() == UISTATE_INGAME) { if (pause != Core_IsStepping()) { // != is xor for bools if (disasmWindow[0]) SendMessage(disasmWindow[0]->GetDlgHandle(), WM_COMMAND, IDC_STOPGO, 0); else Core_EnableStepping(pause); } } if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) { NativeMessageReceived("got_focus", ""); hasFocus = true; trapMouse = true; } if (wParam == WA_INACTIVE) { NativeMessageReceived("lost_focus", ""); WindowsRawInput::LoseFocus(); InputDevice::LoseFocus(); hasFocus = false; trapMouse = false; } } break; case WM_ERASEBKGND: // This window is always covered by DisplayWindow. No reason to erase. return 1; case WM_MOVE: SavePosition(); break; case WM_ENTERSIZEMOVE: inResizeMove = true; break; case WM_EXITSIZEMOVE: inResizeMove = false; HandleSizeChange(SIZE_RESTORED); break; case WM_SIZE: switch (wParam) { case SIZE_RESTORED: case SIZE_MAXIMIZED: if (g_IgnoreWM_SIZE) { return DefWindowProc(hWnd, message, wParam, lParam); } else if (!inResizeMove) { HandleSizeChange(wParam); } if (hasFocus) { InputDevice::GainFocus(); } break; case SIZE_MINIMIZED: Core_NotifyWindowHidden(true); if (!g_Config.bPauseWhenMinimized) { NativeMessageReceived("window minimized", "true"); } InputDevice::LoseFocus(); break; default: break; } break; case WM_TIMER: // Hack: Take the opportunity to also show/hide the mouse cursor in fullscreen mode. switch (wParam) { case TIMER_CURSORUPDATE: CorrectCursor(); return 0; case TIMER_CURSORMOVEUPDATE: hideCursor = true; KillTimer(hWnd, TIMER_CURSORMOVEUPDATE); return 0; } break; // For some reason, need to catch this here rather than in DisplayProc. case WM_MOUSEWHEEL: { int wheelDelta = (short)(wParam >> 16); KeyInput key; key.deviceId = DEVICE_ID_MOUSE; if (wheelDelta < 0) { key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN; wheelDelta = -wheelDelta; } else { key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP; } // There's no separate keyup event for mousewheel events, let's pass them both together. // This also means it really won't work great for key mapping :( Need to build a 1 frame delay or something. key.flags = KEY_DOWN | KEY_UP | KEY_HASWHEELDELTA | (wheelDelta << 16); NativeKey(key); } break; case WM_COMMAND: { if (!MainThread_Ready()) return DefWindowProc(hWnd, message, wParam, lParam); MainWindowMenu_Process(hWnd, wParam); } break; case WM_USER_TOGGLE_FULLSCREEN: ToggleFullscreen(hwndMain, wParam ? true : false); break; case WM_INPUT: return WindowsRawInput::Process(hWnd, wParam, lParam); // TODO: Could do something useful with WM_INPUT_DEVICE_CHANGE? // Not sure why we are actually getting WM_CHAR even though we use RawInput, but alright.. case WM_CHAR: return WindowsRawInput::ProcessChar(hWnd, wParam, lParam); case WM_DEVICECHANGE: #ifndef _M_ARM DinputDevice::CheckDevices(); #endif return DefWindowProc(hWnd, message, wParam, lParam); case WM_VERYSLEEPY_MSG: switch (wParam) { case VERYSLEEPY_WPARAM_SUPPORTED: return TRUE; case VERYSLEEPY_WPARAM_GETADDRINFO: { VerySleepy_AddrInfo *info = (VerySleepy_AddrInfo *)lParam; const u8 *ptr = (const u8 *)info->addr; std::string name; if (MIPSComp::jit && MIPSComp::jit->DescribeCodePtr(ptr, name)) { swprintf_s(info->name, L"Jit::%S", name.c_str()); return TRUE; } if (gpu && gpu->DescribeCodePtr(ptr, name)) { swprintf_s(info->name, L"GPU::%S", name.c_str()); return TRUE; } } return FALSE; default: return FALSE; } break; case WM_DROPFILES: { if (!MainThread_Ready()) return DefWindowProc(hWnd, message, wParam, lParam); HDROP hdrop = (HDROP)wParam; int count = DragQueryFile(hdrop,0xFFFFFFFF,0,0); if (count != 1) { MessageBox(hwndMain,L"You can only load one file at a time",L"Error",MB_ICONINFORMATION); } else { TCHAR filename[512]; if (DragQueryFile(hdrop, 0, filename, 512) != 0) { const std::string utf8_filename = ReplaceAll(ConvertWStringToUTF8(filename), "\\", "/"); NativeMessageReceived("boot", utf8_filename.c_str()); Core_EnableStepping(false); } } } break; case WM_CLOSE: InputDevice::StopPolling(); WindowsRawInput::Shutdown(); return DefWindowProc(hWnd,message,wParam,lParam); case WM_DESTROY: KillTimer(hWnd, TIMER_CURSORUPDATE); KillTimer(hWnd, TIMER_CURSORMOVEUPDATE); PostQuitMessage(0); break; case WM_USER + 1: if (disasmWindow[0]) disasmWindow[0]->NotifyMapLoaded(); if (memoryWindow[0]) memoryWindow[0]->NotifyMapLoaded(); if (disasmWindow[0]) disasmWindow[0]->UpdateDialog(); SetForegroundWindow(hwndMain); break; case WM_USER_SAVESTATE_FINISH: SetCursor(LoadCursor(0, IDC_ARROW)); break; case WM_USER_UPDATE_UI: TranslateMenus(hwndMain, menu); // Update checked status immediately for accelerators. UpdateMenus(); break; case WM_USER_WINDOW_TITLE_CHANGED: UpdateWindowTitle(); break; case WM_USER_BROWSE_BOOT_DONE: BrowseAndBootDone(); break; case WM_USER_BROWSE_BG_DONE: BrowseBackgroundDone(); break; case WM_USER_RESTART_EMUTHREAD: NativeSetRestarting(); InputDevice::StopPolling(); MainThread_Stop(); coreState = CORE_POWERUP; UpdateUIState(UISTATE_MENU); MainThread_Start(g_Config.iGPUBackend == (int)GPUBackend::OPENGL); InputDevice::BeginPolling(); break; case WM_MENUSELECT: // Called when a menu is opened. Also when an item is selected, but meh. UpdateMenus(true); WindowsRawInput::NotifyMenu(); trapMouse = false; break; case WM_EXITMENULOOP: // Called when menu is closed. trapMouse = true; break; // Turn off the screensaver. // Note that if there's a screensaver password, this simple method // doesn't work on Vista or higher. case WM_SYSCOMMAND: { switch (wParam) { case SC_SCREENSAVE: return 0; case SC_MONITORPOWER: return 0; } return DefWindowProc(hWnd, message, wParam, lParam); } default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }