summaryrefslogtreecommitdiff
path: root/indra/llwindow/llwindowwin32.cpp
diff options
context:
space:
mode:
authorAndrey Lihatskiy <alihatskiy@productengine.com>2026-08-13 04:19:15 +0300
committerAndrey Lihatskiy <alihatskiy@productengine.com>2026-08-13 04:19:15 +0300
commitf376c52ee2a22b3b58263d86566394e80626fc1a (patch)
tree012d0585b39ef5cd1eac25f4f783d930ff50dff6 /indra/llwindow/llwindowwin32.cpp
parent21bb60fc65e7fa6c3cf472a29cda220f653af1d8 (diff)
parente3a92a3f8e7d6df3772adcecb037149e6fcf0f3e (diff)
Merge branch 'geenz/linux-to-develop' into marchcat/lua-linux-develop
# Conflicts: # .github/workflows/build.yaml # indra/llinventory/llinventory.cpp # indra/newview/CMakeLists.txt # indra/newview/llappviewer.cpp # indra/newview/llappviewerwin32.cpp # indra/newview/llviewermenu.cpp # indra/newview/llvoiceclient.cpp # indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
Diffstat (limited to 'indra/llwindow/llwindowwin32.cpp')
-rw-r--r--indra/llwindow/llwindowwin32.cpp369
1 files changed, 348 insertions, 21 deletions
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 6ac17f8779..a93a3150e1 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -177,6 +177,19 @@ void show_window_creation_error(const std::string& title)
LL_WARNS("Window") << title << LL_ENDL;
}
+static bool is_thread_from_current_process(DWORD thread_id)
+{
+ HANDLE thread_handle = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, thread_id);
+ if (!thread_handle)
+ {
+ return false;
+ }
+
+ const DWORD process_id = GetProcessIdOfThread(thread_handle);
+ CloseHandle(thread_handle);
+ return process_id == GetCurrentProcessId();
+}
+
HGLRC SafeCreateContext(HDC &hdc)
{
__try
@@ -453,6 +466,16 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
}
});
}
+
+ // For mainWindowProc, it should not unpause watchdog if it was paused
+ void pingWindowTimeout(std::string_view state)
+ {
+ if (mWindowTimeout && mWindowTimeout->started())
+ {
+ mWindowTimeout->setTimeout(WINDOW_TIMEOUT_SEC);
+ mWindowTimeout->ping(state);
+ }
+ }
private:
// These timeout related functions are strictly for the thread.
void resumeTimeout(std::string_view state)
@@ -509,6 +532,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
:
LLWindow(callbacks, fullscreen, flags),
mAbsoluteCursorPosition(false),
+ mReceivedSCClose(false),
mMaxGLVersion(max_gl_version),
mMaxCores(max_cores)
{
@@ -1421,7 +1445,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;
}
@@ -1432,7 +1456,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;
}
@@ -1470,7 +1494,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;
}
@@ -1478,14 +1502,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;
}
@@ -1691,14 +1715,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;
}
@@ -1730,7 +1754,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;
}
@@ -1739,7 +1763,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;
}
@@ -1761,7 +1785,7 @@ 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;
}
@@ -1770,7 +1794,7 @@ const S32 max_format = (S32)num_formats - 1;
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;
}
@@ -1978,7 +2002,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;
@@ -2416,18 +2440,45 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_DEVICECHANGE:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DEVICECHANGE");
+ window_imp->mWindowThread->pingWindowTimeout("WM_DEVICECHANGE");
+
+ // Log detailed device change information
+ std::string change_type = "UNKNOWN";
+ switch (w_param)
+ {
+ case DBT_DEVICEARRIVAL: change_type = "DBT_DEVICEARRIVAL"; break;
+ case DBT_DEVICEREMOVECOMPLETE: change_type = "DBT_DEVICEREMOVECOMPLETE"; break;
+ case DBT_DEVNODES_CHANGED: change_type = "DBT_DEVNODES_CHANGED"; break;
+ case DBT_DEVICEQUERYREMOVE: change_type = "DBT_DEVICEQUERYREMOVE"; break;
+ case DBT_DEVICEQUERYREMOVEFAILED: change_type = "DBT_DEVICEQUERYREMOVEFAILED"; break;
+ case DBT_DEVICEREMOVEPENDING: change_type = "DBT_DEVICEREMOVEPENDING"; break;
+ case DBT_CONFIGCHANGED: change_type = "DBT_CONFIGCHANGED"; break;
+ }
+
if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL)
{
- WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp));
+ WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp, change_type));
return 1;
}
+ else if (l_param)
+ {
+ const auto* hdr = reinterpret_cast<const DEV_BROADCAST_HDR*>(l_param);
+ if (hdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
+ {
+ // Might need to register for monitor device notifications
+ // to get this message when monitor is suspended or resumed.
+ // TODO: log monitor suspending and resuming.
+ LL_INFOS("Window") << "DEVICEINTERFACE: " << change_type << LL_ENDL;
+ }
+ }
break;
}
case WM_PAINT:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_PAINT");
+ window_imp->mWindowThread->pingWindowTimeout("WM_PAINT");
GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE);
update_width = update_rect.right - update_rect.left + 1;
update_height = update_rect.bottom - update_rect.top + 1;
@@ -2436,6 +2487,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
update_width, update_height));
break;
}
+ case WM_ERASEBKGND:
+ {
+ RECT client_rect;
+ if (GetClientRect(h_wnd, &client_rect))
+ {
+ FillRect((HDC)w_param, &client_rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
+ }
+ return 1;
+ }
case WM_PARENTNOTIFY:
{
break;
@@ -2473,11 +2533,24 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_ACTIVATEAPP:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ACTIVATEAPP");
+ // Resolve ownership before deferring the work because thread IDs can
+ // be reused after a thread exits.
+ const bool activating_same_process_thread =
+ !w_param && is_thread_from_current_process(static_cast<DWORD>(l_param));
window_imp->post([=]()
{
// This message should be sent whenever the app gains or loses focus.
BOOL activating = (BOOL)w_param;
+ // Native dialogs run on a worker thread. Moving focus between
+ // the viewer and one of those dialogs must not be treated as
+ // switching to another application: in fullscreen that would
+ // minimize the viewer and hide its owned dialog.
+ if (!activating && activating_same_process_thread)
+ {
+ activating = TRUE;
+ }
+
if (window_imp->mFullscreen)
{
// When we run fullscreen, restoring or minimizing the app needs
@@ -2527,8 +2600,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;
@@ -2543,9 +2623,31 @@ 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->mWindowThread->pingWindowTimeout("WM_CLOSE");
+
+ 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?
@@ -2567,6 +2669,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.
@@ -2580,6 +2692,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
// Comes after WM_QUERYENDSESSION
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENDSESSION");
LL_INFOS("Window") << "Received WM_ENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
+ window_imp->mWindowThread->pingWindowTimeout("WM_ENDSESSION");
unsigned int end_session_flags = (U32)l_param;
if (w_param == TRUE // if true, session is ending
@@ -2588,8 +2701,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))
{
@@ -2608,6 +2723,193 @@ 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.
+
+ // Mark app as potentially closing, to minimize issues if OS does not recover.
+ window_imp->mCallbacks->handlePreCloseRequest();
+ window_imp->post([=]()
+ {
+ window_imp->mCallbacks->handleSuspendRequest();
+ });
+ // Window thread normally doesn't block main thread, but OS can suspend
+ // immediately if we don't wait.
+ // Keep OS from suspending to give a chance to send stats.
+ ms_sleep(1000);
+ return TRUE;
+
+ case PBT_APMRESUMESUSPEND:
+ LL_INFOS("Window") << "System is resuming from suspend" << LL_ENDL;
+ window_imp->mCallbacks->handleCloseRequestCanceled();
+ 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:
+ LL_INFOS("Window") << "Received WM_POWERBROADCAST with wParam: 0x" << std::hex << (uintptr_t)w_param << " lParam: 0x" << (uintptr_t)l_param << std::dec << LL_ENDL;
+ 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");
@@ -3115,6 +3417,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_DPICHANGED:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DPICHANGED");
+ window_imp->mWindowThread->pingWindowTimeout("WM_DPICHANGED");
LPRECT lprc_new_scale;
F32 new_scale = F32(LOWORD(w_param)) / F32(USER_DEFAULT_SCREEN_DPI);
lprc_new_scale = (LPRECT)l_param;
@@ -3135,7 +3438,19 @@ 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->mWindowThread->pingWindowTimeout("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:
@@ -3173,6 +3488,9 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
case WM_SETTINGCHANGE:
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETTINGCHANGE");
+ // Can be called on OS user switching
+ LL_INFOS("Window") << "WM_SETTINGCHANGE, with wParam: 0x" << std::hex << (uintptr_t)w_param << " lParam: 0x" << (uintptr_t)l_param << std::dec << LL_ENDL;
+ window_imp->mWindowThread->pingWindowTimeout("WM_SETTINGCHANGE");
if (w_param == SPI_SETMOUSEVANISH)
{
if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &window_imp->mMouseVanish, 0))
@@ -5035,6 +5353,13 @@ inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread()
: LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, false)
{
LL::ThreadPool::start();
+
+ // Set thread name for the window thread
+ // This will make it distinguishable in Visual Studio debugger
+ post([this]()
+ {
+ SetThreadDescription(GetCurrentThread(), L"LLWindowWin32 Thread");
+ });
}
/**
@@ -5216,7 +5541,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()
}
// Normally won't exist yet, but in case of re-init, make sure it's cleaned up
- resumeTimeout("WindowThread");
+ resumeTimeout("Window:WindowThread");
while (! getQueue().done())
{
@@ -5227,23 +5552,25 @@ void LLWindowWin32::LLWindowWin32Thread::run()
if (mWindowHandleThrd != 0)
{
- pingTimeout("messages");
MSG msg;
BOOL status;
if (mhDCThrd == 0)
{
+ pingTimeout("Window:PeekMessage");
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - PeekMessage");
logger.onChange("PeekMessage(", std::hex, mWindowHandleThrd, ")");
status = PeekMessage(&msg, mWindowHandleThrd, 0, 0, PM_REMOVE);
}
else
{
+ pingTimeout("Window:GetMessage");
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - GetMessage");
logger.always("GetMessage(", std::hex, mWindowHandleThrd, ")");
status = GetMessage(&msg, NULL, 0, 0);
}
if (status > 0)
{
+ pingTimeout("Window:TranslateMessage");
logger.always("got MSG (", std::hex, msg.hwnd, ", ", msg.message,
", ", msg.wParam, ")");
TranslateMessage(&msg);
@@ -5255,7 +5582,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()
{
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Function Queue");
- pingTimeout("queue");
+ pingTimeout("Window:Queue");
logger.onChange("runPending()");
//process any pending functions
getQueue().runPending();