diff options
| author | Jonathan "Geenz" Goodman <geenz@lindenlab.com> | 2026-05-20 12:05:18 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-20 12:05:18 -0400 |
| commit | 40cd1617a6a663fb921265101307454b490ec4ba (patch) | |
| tree | 2f13741295c385172364e3fa940d7591f32fac4e /indra/llwindow/llwindowwin32.cpp | |
| parent | 9c2ee024bef564ebb6e2e8942b43ffae1d99232c (diff) | |
| parent | df844170600608b37c2725f7a263f1de3b055618 (diff) | |
Merge branch 'develop' into geenz/26.3-to-develop
Diffstat (limited to 'indra/llwindow/llwindowwin32.cpp')
| -rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 271 |
1 files changed, 254 insertions, 17 deletions
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index c185fc6c4a..52e0cdd5d5 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -508,6 +508,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, : LLWindow(callbacks, fullscreen, flags), mAbsoluteCursorPosition(false), + mReceivedSCClose(false), mMaxGLVersion(max_gl_version), mMaxCores(max_cores) { @@ -1420,7 +1421,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo catch (...) { LOG_UNHANDLED_EXCEPTION("ChoosePixelFormat"); - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1431,7 +1432,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1469,7 +1470,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo if (!SetPixelFormat(mhDC, pixel_format, &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1477,14 +1478,14 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo if (!(mhRC = SafeCreateContext(mhDC))) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } if (!wglMakeCurrent(mhDC, mhRC)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1690,14 +1691,14 @@ const S32 max_format = (S32)num_formats - 1; if (!mhDC) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBDevContextErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBDevContextErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } if (!SetPixelFormat(mhDC, pixel_format, &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtSetErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1729,7 +1730,7 @@ const S32 max_format = (S32)num_formats - 1; { LL_WARNS("Window") << "No wgl_ARB_pixel_format extension!" << LL_ENDL; // cannot proceed without wgl_ARB_pixel_format extension, shutdown same as any other gGLManager.initGL() failure - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1738,7 +1739,7 @@ const S32 max_format = (S32)num_formats - 1; if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBPixelFmtDescErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1760,14 +1761,14 @@ const S32 max_format = (S32)num_formats - 1; if (!wglMakeCurrent(mhDC, mhRC)) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextActErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } if (!gGLManager.initGL()) { - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); close(); return false; } @@ -1975,7 +1976,7 @@ void* LLWindowWin32::createSharedContext() if (!rc && !(rc = wglCreateContext(mhDC))) { close(); - LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), 8/*LAST_EXEC_GRAPHICS_INIT*/); + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBGLContextErr"), LLError::LLUserWarningMsg::ERROR_INIT_FAILED); } return rc; @@ -2524,8 +2525,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_SYSCOMMAND: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SYSCOMMAND"); - switch (w_param) + switch (w_param & 0xFFF0) { + case SC_CLOSE: + // User clicked close from system menu/taskbar or 'end process' from task manager + // Do nothing, will cause WM_CLOSE. + // If we don't get this message before WM_CLOSE, we are likely getting + // a kill from some external program. Win11 task manager Does cause SC_CLOSE. + window_imp->mReceivedSCClose = true; + break; case SC_KEYMENU: // Disallow the ALT key from triggering the default system menu. return 0; @@ -2540,9 +2548,30 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_CLOSE: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE"); - // todo: WM_CLOSE can be caused by user and by task manager, - // distinguish these cases. - // For now assume it is always user. + + window_imp->mCallbacks->handlePreCloseRequest(); // mark app as potentially closing + if (!window_imp->mReceivedSCClose) + { + // Some external program is trying to close the app. + // Assume that it's going to destroy process if it fails + // and try to fast-quit without confirmation or cleanup. + window_imp->post([=]() + { + // Check if app needs cleanup or can be closed immediately. + if (window_imp->mCallbacks->handleSessionExit(window_imp)) + { + // Get the app to initiate cleanup. + window_imp->mCallbacks->handleQuit(window_imp); + } + }); + return 0; + } + window_imp->mReceivedSCClose = false; + + // There is no way to tell the difference between a user issued + // WM_CLOSE or task manager's WM_CLOSE. + // Assume it is a user and ask for confirmation, but create a marker file. + // If App keeps doing something after a second, or gets 'destroy' message clear the marker. window_imp->post([=]() { // Will the app allow the window to close? @@ -2564,6 +2593,16 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ } return 0; } + case WM_NCDESTROY: + LL_INFOS("Window") << "Received WM_NCDESTROY" << LL_ENDL; + break; + case WM_WTSSESSION_CHANGE: + { + // Detects Remote Desktop disconnects, fast user switching, session logoff + // w_param: WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT, WTS_SESSION_LOGOFF, etc. + LL_INFOS("Window") << "Received WM_WTSSESSION_CHANGE with wParam: " << (U32)w_param << LL_ENDL; + break; + } case WM_QUERYENDSESSION: { // Generally means that OS is going to shut down or user is going to log off. @@ -2585,8 +2624,10 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ || (end_session_flags & ENDSESSION_CRITICAL) // will shutdown regardless of app state || (end_session_flags & ENDSESSION_LOGOFF)) // logoff, can delay shutdown { + window_imp->mCallbacks->handlePreCloseRequest(); // mark app as closing window_imp->post([=]() { + LL_INFOS("Window") << "Shutting down due to session terminating" << LL_ENDL; // Check if app needs cleanup or can be closed immediately. if (window_imp->mCallbacks->handleSessionExit(window_imp)) { @@ -2605,6 +2646,191 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // if session is ending OS is going to take care of it. return 0; } + case WM_POWERBROADCAST: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_POWERBROADCAST"); + switch (w_param) + { + case PBT_APMSUSPEND: + LL_INFOS("Window") << "System is suspending (sleep/hibernate)" << LL_ENDL; + // System is about to enter sleep or hibernation + // Viewer can't function in hibernation, try to shut down. + // The system allows approximately two seconds for an + // application to handle this notification. + window_imp->post([=]() + { + LL_INFOS("Window") << "Shutting down due to system suspending (sleep/hibernate)" << LL_ENDL; + if (window_imp->mCallbacks->handleSessionExit(window_imp)) + { + // Get the app to initiate cleanup. + window_imp->mCallbacks->handleQuit(window_imp); + } + }); + ms_sleep(1000); + return TRUE; + + case PBT_APMRESUMESUSPEND: + LL_INFOS("Window") << "System is resuming from suspend" << LL_ENDL; + // Shouldn't be up, but log just in case. + return TRUE; + + case PBT_APMPOWERSTATUSCHANGE: + LL_INFOS("Window") << "Power status has changed" << LL_ENDL; + // Power status change (AC/battery) + // Viewer requires high performance, not much we can do. + // about it, but log for diagnostic purposes (example: + // OS trying to throw viewer at an iGPU after this message) + return TRUE; + + default: + break; + } + break; + } + case WM_POST_UNINSTALL_: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_POST_UNINSTALL_"); + // Other instance, likely velopack, requested we quit. + // Don't trust PID alone (can be spoofed), verify the + // path for security purposes before processing. + // Verifying path isn't a strong varranty, if this turns + // up to be a risk, we will want something more secure. + // See sendShutdownToOtherInstances for the sender. + + // LPARAM contains message type. + DWORD message_type = static_cast<DWORD>(l_param); + if (message_type == WM_POST_UNINSTALL_MSG_SHUTDOWN || message_type == WM_POST_UNINSTALL_MSG_UPDATE) + { + DWORD sender_process_id = static_cast<DWORD>(w_param); + + // Make sure something didn't just send us our own process + DWORD our_process_id = GetCurrentProcessId(); + if (our_process_id == sender_process_id) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ from our own process, ignoring" << LL_ENDL; + break; + } + + if (sender_process_id == 0) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ but couldn't get sender process ID" << LL_ENDL; + break; + } + + // Open the existing sender process to verify its executable path + HANDLE hSenderProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, sender_process_id); + if (!hSenderProcess) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ but couldn't open sender process" << LL_ENDL; + break; + } + + // Get the actual executable path of the sender + wchar_t sender_exe_path[MAX_PATH]; + DWORD size = MAX_PATH; + bool got_sender_path = QueryFullProcessImageNameW(hSenderProcess, 0, sender_exe_path, &size) != 0; + CloseHandle(hSenderProcess); + + if (!got_sender_path) + { + LL_WARNS("Window") << "Received WM_POST_UNINSTALL_ but couldn't query sender executable path" << LL_ENDL; + break; + } + + // Extract directory from sender's executable path + wchar_t sender_dir[MAX_PATH]; + wchar_t* file_part = nullptr; + DWORD result = GetFullPathNameW(sender_exe_path, MAX_PATH, sender_dir, &file_part); + + if (result == 0 || result >= MAX_PATH) + { + LL_WARNS("Window") << "Failed to normalize sender executable path" << LL_ENDL; + break; + } + + // Remove the filename to get just directory + if (file_part) + { + *file_part = L'\0'; + } + + // Remove trailing backslash + size_t sender_dir_len = wcslen(sender_dir); + if (sender_dir_len > 0 && sender_dir[sender_dir_len - 1] == L'\\') + { + sender_dir[sender_dir_len - 1] = L'\0'; + sender_dir_len--; + } + + // Remove "\current" suffix from sender's path if present + const std::wstring current_suffix = L"\\current"; + std::wstring sender_normalized_str(sender_dir); + if (sender_normalized_str.length() >= current_suffix.length() && + _wcsicmp(sender_normalized_str.c_str() + sender_normalized_str.length() - current_suffix.length(), + current_suffix.c_str()) == 0) + { + sender_normalized_str.resize(sender_normalized_str.length() - current_suffix.length()); + } + + // Get our executable directory for comparison + std::wstring our_wide = ll_convert<std::wstring>(gDirUtilp->getExecutableDir()); + + // Normalize our path + wchar_t our_normalized[MAX_PATH]; + file_part = nullptr; + + DWORD result2 = GetFullPathNameW(our_wide.c_str(), MAX_PATH, our_normalized, &file_part); + + if (result2 == 0 || result2 >= MAX_PATH) + { + LL_WARNS("Window") << "Failed to normalize our executable path" << LL_ENDL; + break; + } + + // Remove trailing backslash + size_t our_len = wcslen(our_normalized); + if (our_len > 0 && our_normalized[our_len - 1] == L'\\') + { + our_normalized[our_len - 1] = L'\0'; + our_len--; + } + + // Remove "\current" suffix from our path if present + std::wstring our_normalized_str(our_normalized); + if (our_normalized_str.length() >= current_suffix.length() && + _wcsicmp(our_normalized_str.c_str() + our_normalized_str.length() - current_suffix.length(), + current_suffix.c_str()) == 0) + { + our_normalized_str.resize(our_normalized_str.length() - current_suffix.length()); + } + + // Compare the normalized base installation paths (case-insensitive) + if (_wcsicmp(sender_normalized_str.c_str(), our_normalized_str.c_str()) == 0) + { + window_imp->post([=]() + { + LL_INFOS("Window") << "Received valid shutdown request from verified same installation directory" << LL_ENDL; + // Check if app needs cleanup or can be closed immediately. + if (window_imp->mCallbacks->handleCloseRequest(window_imp, false)) + { + // Get the app to initiate cleanup. + window_imp->mCallbacks->handleQuit(window_imp); + } + }); + } + else + { + LL_WARNS("Window") << "Rejected shutdown request - sender not from our installation directory. " + << "Sender: " << ll_convert_wide_to_string(sender_normalized_str) + << " Our: " << ll_convert_wide_to_string(our_normalized_str) << LL_ENDL; + } + } + else + { + LL_WARNS("Window") << "Received invalid WM_POST_UNINSTALL_ message" << LL_ENDL; + } + break; + } case WM_COMMAND: { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND"); @@ -3132,7 +3358,18 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_DISPLAYCHANGE: { - WINDOW_IMP_POST(window_imp->mCallbacks->handleDisplayChanged()); + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DISPLAYCHANGE"); + window_imp->post([=]() { + window_imp->mCallbacks->handleDisplayChanged(); + // Note: WM_DISPLAYCHANGE was passing to WM_SETFOCUS + // which might have been unintended and was messing with zones. + // handleFocus was copied over and return 0 added, but + // handleFocus might be not needed here. + // handleFocus resets mouse, closes popups and keys, which + // we probablt should do on 'display change'. + window_imp->mCallbacks->handleFocus(window_imp); + }); + return 0; } case WM_SETFOCUS: |
