Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenConsole.exe can steal focus from the terminal #17168

Open
tusharsnx opened this issue May 1, 2024 · 10 comments
Open

OpenConsole.exe can steal focus from the terminal #17168

tusharsnx opened this issue May 1, 2024 · 10 comments
Labels
Area-User Interface Issues pertaining to the user interface of the Console or Terminal Issue-Bug It either shouldn't be doing this or needs an investigation. Product-Terminal The new Windows Terminal.

Comments

@tusharsnx
Copy link
Contributor

tusharsnx commented May 1, 2024

Windows Terminal version

Windows Terminal Dev

Windows build number

10.0.22631.0

Other Software

No response

Steps to reproduce

  1. Open an instance of WT and keep it in the background (without focus).
  2. In Visual Studio, make a scratch project (code doesn't affect repro):
#include <iostream>
int main() {
	std::cout << "hello world";
	std::cin.get(); // to keep the program from exiting
	return 0;
}
  1. Start the program by clicking on the green play button (Start without debugging).
  2. WT starts but without focus.

Expected Behavior

WT starts with focus.

Actual Behavior

WT starts without focus.

@tusharsnx tusharsnx added Issue-Bug It either shouldn't be doing this or needs an investigation. Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels May 1, 2024
Copy link

github-actions bot commented May 1, 2024

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Open similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

@tusharsnx
Copy link
Contributor Author

tusharsnx commented May 1, 2024

Trace

I repeated step (3) 4 times to get better data, and indeed three instances of openconsole.exe, and all of them stole focus from the terminal just after starting up. The 3rd time it didn't steal the focus, proving that it might not always happen.

image

I was investigating microsoft/wslg#1212 which also deals with the same area of focus lost issue. They shared a trace profile .wprp which other than collecting other data, also gathers window focus traces, and it helped me discover that OpenConsole.exe sometimes steals focus.

foregroundTracing1.zip

@zadjii-msft
Copy link
Member

I'm guessing what you've done here is basically investigate #13388. That bug has been my white whale for... really some long time now. I've spent months trying to trace or get consistent enough repros to find something out.

I'm 99% sure the code that creates that window is in

[[nodiscard]] NTSTATUS InteractivityFactory::CreatePseudoWindow(HWND& hwnd, const HWND owner)
. @ekoschik looked into this for a while to try and figure out why that WS_POPUP, WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE window still gets foreground - because it doesn't make any sense.

Maybe you'll have better luck? If you've got a consistent repro, then maybe there's more traces we could have you collect? I'll reach out to him and see.

@zadjii-msft
Copy link
Member

#16014 also had some windowing changes that I wanted to experiment with, but I also didn't have a consistent repro, so they were all guesses. If you've got a consistent repro, maybe you might be able to get a better signal out of that PR?

(though, admittedly, that PR was mostly there to address the tiny boxes that sometimes show up in the top-right corner of the monitor, ala #15219)

@lhecker
Copy link
Member

lhecker commented May 1, 2024

re: #16014 (comment)
As an experiment we could still try to turn it into a HWND_MESSAGE window in e.g. the 1.22 canary. I know you said that it needs WS_OVERLAPPEDWINDOW because otherwise it can't be minimized but in my testing a HWND_MESSAGE could receive SW_MINIMIZE just fine. If it doesn't work after all, we'll at least know for certain that HWND_MESSAGE cannot be used after all.

@zadjii-msft
Copy link
Member

in my testing a HWND_MESSAGE could receive SW_MINIMIZE just fine. If it doesn't work after all, we'll at least know for certain that HWND_MESSAGE cannot be used after all

I think part of the problem (and this is me trying to remember what Evan was explaining a year back) was that it can receive a SW_MINIMIZE, but the window isn't actually minimized. Like, message windows have different state that's tracked by user32. So if you query a message window after a SW_MINIMIZE, user32 doesn't actually think that window's minimized. Something like that? (it's been a while now).

I forget why we didn't ever merge that PR in the past, but we should totally just do that as soon as 1.21 forks

@lhecker
Copy link
Member

lhecker commented May 1, 2024

(Oh, it may be worth mentioning for other readers of this issue that I responded over in the mentioned PR, where I felt like it's more appropriate. Here: #16014 (comment))

@carlos-zamora carlos-zamora added Area-User Interface Issues pertaining to the user interface of the Console or Terminal Product-Terminal The new Windows Terminal. and removed Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting labels May 1, 2024
@carlos-zamora carlos-zamora added this to the Terminal v1.21 milestone May 1, 2024
@tusharsnx
Copy link
Contributor Author

tusharsnx commented May 3, 2024

Based on #16014, I tried all window (extended/non-extended) styles in islolation, and nothing seems to stop the child window from stealing parent focus. What am I missing here?

Code
#include <windows.h>
#include "string"

static constexpr auto parentWindowClass = L"ParentWindow";
static constexpr auto childWindowClass = L"ChildWindow";

LRESULT static _stdcall WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_DESTROY)
    {
        PostQuitMessage(0);
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

ATOM MyRegisterClass(const std::wstring& szWindowClass)
{
    WNDCLASSEXW wcex{ 0 };
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc = WndProc;
    wcex.cbWndExtra = 0;
    wcex.lpszClassName = &szWindowClass[0];

    return RegisterClassExW(&wcex);
}


int _stdcall wWinMain(HINSTANCE hInstance,
                      HINSTANCE /*hPrevInstance*/,
                      LPWSTR    /*lpCmdLine*/,
                      int       nCmdShow)
{

    const auto parentClassName = reinterpret_cast<LPCWSTR>(MyRegisterClass(parentWindowClass));
    const auto childClassName = reinterpret_cast<LPCWSTR>(MyRegisterClass(childWindowClass));

    // Parent window
    HWND hWnd = CreateWindowW(parentClassName,
        nullptr,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        nullptr,
        nullptr,
        nullptr,
        nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    // Child window

    // using WS_POPUP makes the window invisible, but doesn't stop the focus shifting.
    const auto styles = WS_OVERLAPPED | (WS_MINIMIZEBOX | WS_SYSMENU);

    // - using WS_EX_LAYERED makes the window invisible, but doesn't stop the focus shifting.
    // - WS_EX_NOACTIVATE doesn't stop the focus shifting too.
    const auto exStyles = WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;

    HWND childHWnd = CreateWindowExW(exStyles,
        childClassName,
        nullptr,
        styles,
        0,
        0,
        0,
        0,
        hWnd,
        nullptr,
        nullptr,
        nullptr);

    ShowWindow(childHWnd, nCmdShow);
    UpdateWindow(childHWnd);

    // SetForegroundWindow(hWnd);

    // This SETS parent (not ownership) even when WS_CHILD isn't used???
    //
    // SetParent also seems to put the focus to the child hwnd. Another opportunity for focus shifting.
    // SetParent(childHWnd, hWnd); 

    // Message loop
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }

    return (int) msg.wParam;
}

@lhecker
Copy link
Member

lhecker commented May 3, 2024

I believe the problem is that you need to pass SW_SHOWNOACTIVATE for the child's ShowWindow call. That's what we do in conhost's ITerminalApi:

::ShowWindowAsync(hwnd, showOrHide ? SW_SHOWNOACTIVATE : SW_MINIMIZE);

@zadjii-msft
Copy link
Member

wait @tusharsnx did the 👍 you gave on #17168 (comment) actually mean "that worked"? I feel like we've been at a loss for so long on this, that I'm just desperate for any progress at all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-User Interface Issues pertaining to the user interface of the Console or Terminal Issue-Bug It either shouldn't be doing this or needs an investigation. Product-Terminal The new Windows Terminal.
Projects
None yet
Development

No branches or pull requests

4 participants