Fix desktop designer click handling and add goHome API

Designer click handling:
- Fix DPI scaling in MouseButtonCallback and CursorPosCallback
- Scale coordinates from window space to framebuffer/RmlUi context
- Remove window resizing in ResizeToPhone (caused DPI mismatches)

Test framework:
- Fix SendMouseDown to use MOUSEEVENTF_MOVE before button down
- Remove double-scaling in ScaleToPhysical (WindowController handles it)
- All 5 UI navigation tests now pass

Kernel API:
- Add goHome() Lua function to return to home screen
- Stops any running third-party apps before navigating

Test app:
- Update sandbox-test to use goHome() instead of goBack()

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 14:52:15 +01:00
parent d6b7504408
commit 984e8715d7
5 changed files with 160 additions and 76 deletions

View File

@@ -107,33 +107,47 @@ LPARAM WindowController::PhoneToClientLParam(int phoneX, int phoneY) {
bool WindowController::SendMouseDown(int phoneX, int phoneY) {
if (!m_hwnd) return false;
// Convert phone coordinates to client coordinates
// Convert phone coordinates to client coordinates (using window scale)
int clientX = static_cast<int>(phoneX * m_info.scaleX);
int clientY = static_cast<int>(phoneY * m_info.scaleY);
// Calculate screen coordinates
int screenX = m_info.clientX + clientX;
int screenY = m_info.clientY + clientY;
// Get DPI info for debugging
UINT dpi = GetDpiForWindow(m_hwnd);
// Calculate screen coordinates from client position
// On DPI-aware systems, Windows APIs return consistent coordinate spaces
int screenX = m_info.clientX + clientX;
int screenY = m_info.clientY + clientY;
// Ensure window is foreground before clicking
SetForegroundWindow(m_hwnd);
Sleep(10); // Small delay
Sleep(10);
// Use SendInput for GLFW compatibility
SetCursorPos(screenX, screenY);
Sleep(10); // Small delay for cursor move
// Get screen dimensions for absolute coordinate conversion
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
INPUT input = {};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &input, sizeof(INPUT));
// Convert to normalized absolute coordinates (0-65535)
LONG absX = static_cast<LONG>((screenX * 65535) / screenWidth);
LONG absY = static_cast<LONG>((screenY * 65535) / screenHeight);
std::cout << "MouseDown at phone(" << phoneX << "," << phoneY << ") -> screen("
<< screenX << "," << screenY << ") dpi=" << dpi << std::endl;
// Send mouse move then button down
INPUT moveInput = {};
moveInput.type = INPUT_MOUSE;
moveInput.mi.dx = absX;
moveInput.mi.dy = absY;
moveInput.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
SendInput(1, &moveInput, sizeof(INPUT));
Sleep(20);
INPUT clickInput = {};
clickInput.type = INPUT_MOUSE;
clickInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &clickInput, sizeof(INPUT));
std::cout << "MouseDown at phone(" << phoneX << "," << phoneY << ") -> client("
<< clientX << "," << clientY << ") screen(" << screenX << "," << screenY
<< ") dpi=" << dpi << std::endl;
return true;
}
@@ -226,38 +240,20 @@ bool WindowController::Close() {
bool WindowController::ResizeToPhone() {
if (!m_hwnd) return false;
// Get current window rect to preserve position
// Skip resizing - the designer creates the window at the correct size
// and resizing causes DPI scaling mismatches between window and framebuffer.
// Just move window to top-left for consistent test positioning.
RECT currentRect;
::GetWindowRect(m_hwnd, &currentRect);
int currentWidth = currentRect.right - currentRect.left;
int currentHeight = currentRect.bottom - currentRect.top;
// Calculate window size needed for phone-sized client area
RECT desiredRect = {0, 0, PHONE_WIDTH, PHONE_HEIGHT};
DWORD style = ::GetWindowLong(m_hwnd, GWL_STYLE);
DWORD exStyle = ::GetWindowLong(m_hwnd, GWL_EXSTYLE);
::AdjustWindowRectEx(&desiredRect, style, FALSE, exStyle);
std::cout << "ResizeToPhone: keeping current size " << currentWidth << "x" << currentHeight
<< ", moving to (0,0)" << std::endl;
int newWidth = desiredRect.right - desiredRect.left;
int newHeight = desiredRect.bottom - desiredRect.top;
// Check screen dimensions
int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
std::cout << "ResizeToPhone: screen=" << screenWidth << "x" << screenHeight
<< ", needed=" << newWidth << "x" << newHeight << std::endl;
// If screen is too small, we can't resize to full phone size
if (newHeight > screenHeight) {
std::cout << " Warning: Screen too small for full phone resolution, keeping current size" << std::endl;
return true; // Not an error, just can't resize
}
// Move window to top-left to ensure it fits on screen
int newX = 0;
int newY = 0;
std::cout << " Moving to (" << newX << "," << newY << ") size " << newWidth << "x" << newHeight << std::endl;
::MoveWindow(m_hwnd, newX, newY, newWidth, newHeight, TRUE);
// Move window to top-left for consistent positioning
::MoveWindow(m_hwnd, 0, 0, currentWidth, currentHeight, TRUE);
// Re-acquire window info
std::this_thread::sleep_for(std::chrono::milliseconds(100));
@@ -274,26 +270,36 @@ bool WindowController::SendClickFromBottom(int clientX, int offsetFromBottom) {
int screenX = m_info.clientX + clientX;
int screenY = m_info.clientY + clientY;
// Save current cursor position
POINT oldPos;
GetCursorPos(&oldPos);
// Ensure window is active
SetForegroundWindow(m_hwnd);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
// Move cursor and click
SetCursorPos(screenX, screenY);
// Get screen dimensions for absolute coordinate conversion
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
INPUT input = {};
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &input, sizeof(INPUT));
// Convert to normalized absolute coordinates (0-65535)
LONG absX = static_cast<LONG>((screenX * 65535) / screenWidth);
LONG absY = static_cast<LONG>((screenY * 65535) / screenHeight);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
// Use SendInput with absolute move + click
INPUT inputs[3] = {};
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &input, sizeof(INPUT));
// Move cursor
inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dx = absX;
inputs[0].mi.dy = absY;
inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
// Mouse down
inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
// Mouse up
inputs[2].type = INPUT_MOUSE;
inputs[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(3, inputs, sizeof(INPUT));
std::cout << "ClickFromBottom at client(" << clientX << "," << clientY
<< ") -> screen(" << screenX << "," << screenY << ")" << std::endl;