How to show another window on FSE(FullScreen Exclusive)

Saito, Makoto (SIE) 20 Reputation points
2024-05-02T16:39:51.1233333+00:00

私たちは FullScreen Exclusive (FSE) な画面を持つアプリケーションを開発中です。

FSE は SetFullscreenState(TRUE, nullptr) を呼び出すよう実装しています。

FSE上でCreateWindowEx() して、別のウィンドウを表示したいのですが、それは可能でしょうか?

https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/dxgi-1-4-improvements

には以下のように書いてあり、可能なようにも読み取れるのですが。

  • SetFullscreenState no longer exclusively owns the display, so user-initiated operating system elements can seamlessly appear in front of application output. Volume settings is an example of this.

手元で確認しているのは、CreateWindowEx() に渡す style に WS_OVERLAPPED が含まれていると、FSEではなくなりました。

WS_CHILD を渡すと、FSEを保ったままウィンドウを作れることは確認しました。

できれば、WS_OVERLAPPED を指定したいですが、それは可能でしょうか?

WS_CHILDを渡す以外に、FSEのままウィンドウを表示する方法はあるでしょうか?

Windows
Windows
A family of Microsoft operating systems that run across personal computers, tablets, laptops, phones, internet of things devices, self-contained mixed reality headsets, large collaboration screens, and other devices.
4,843 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,448 questions
{count} votes

Accepted answer
  1. Tong Xu - MSFT 2,036 Reputation points Microsoft Vendor
    2024-05-09T08:15:14.02+00:00

    Hi! I bring good news!
    I succeed in keeping FSE when making a child window with WS_OVERLAPPEDWINDOW.
    Using GetDeviceCaps in SetWindowPos to set full screen mode but cannot set background color of child window.

    #include <Windows.h>
    #include <windowsx.h>
    #include <dwmapi.h>
    #include"Resource.h"
    #pragma comment(lib, "dwmapi.lib")
    LRESULT CALLBACK  WndProc2(HWND, UINT, WPARAM, LPARAM);
    LRESULT hitTest(HWND hwnd, POINT cursor) {
        const POINT border{
            ::GetSystemMetrics(SM_CXFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER),
            ::GetSystemMetrics(SM_CYFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER)
        };
        RECT winRect;
        if (!::GetWindowRect(hwnd, &winRect)) return HTNOWHERE;
        enum region_mask {
            client = 0b0000,
            left = 0b0001,
            right = 0b0010,
            top = 0b0100,
            bottom = 0b1000,
        };
        const auto result = left * (cursor.x < (winRect.left + border.x)) |
            right * (cursor.x >= (winRect.right - border.x)) |
            top * (cursor.y < (winRect.top + border.y)) |
            bottom * (cursor.y >= (winRect.bottom - border.y));
        switch (result) {
        case left: return HTLEFT;
        case right: return HTRIGHT;
        case top: return HTTOP;
        case bottom: return HTBOTTOM;
        case top | left: return HTTOPLEFT;
        case top | right: return HTTOPRIGHT;
        case bottom | left: return HTBOTTOMLEFT;
        case bottom | right: return HTBOTTOMRIGHT;
        case client: return HTCAPTION;
        default: return HTNOWHERE;
        }
    }
    LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
        switch (msg) {
       
        case WM_NCHITTEST: {
            return hitTest(hwnd, POINT{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) });
        }
        case WM_CLOSE: {
            ::DestroyWindow(hwnd);
            return 0;
        }
        case WM_DESTROY: {
            PostQuitMessage(0);
            return 0;
        }
        }
        return ::DefWindowProcW(hwnd, msg, wparam, lparam);
    }
    int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
        WNDCLASSEXW wcx{};
        wcx.cbSize = sizeof(wcx);
        wcx.style = CS_HREDRAW | CS_VREDRAW;
        wcx.hInstance = nullptr;
        wcx.lpfnWndProc = &wndProc;
        wcx.lpszClassName = L"BorderlessWindowClass";
        wcx.hbrBackground = CreateSolidBrush(RGB(240, 160, 160));
        wcx.hCursor = ::LoadCursorW(nullptr, IDC_ARROW);
        ::RegisterClassExW(&wcx);
        WNDCLASSEX wcex;
        wcex.cbSize = sizeof(WNDCLASSEX);
        wcex.style = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc = WndProc2;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = 0;
        wcex.hInstance = nullptr;
        wcex.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_FULLSCREEN));
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wcex.hbrBackground = CreateSolidBrush(RGB(20, 20, 160));
        wcex.lpszMenuName = MAKEINTRESOURCE(IDC_FULLSCREEN);
        wcex.lpszClassName = L"BorderlessWindowClass";;
        wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
     
        auto borderlessStyle =  WS_VISIBLE;
        HWND hwnd = CreateWindowExW(0, wcx.lpszClassName, L"window", borderlessStyle, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr, nullptr, nullptr, nullptr);
        HWND hwnd2 = CreateWindowExW(0, wcex.lpszClassName, L"windows2", WS_CHILD| WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, hwnd, nullptr, nullptr, nullptr);
        ShowWindow(hwnd, SW_SHOW);
        UpdateWindow(hwnd);
        ShowWindow(hwnd2, SW_SHOW);
        UpdateWindow(hwnd2);
        ::SetWindowLongPtrW(hwnd, GWL_STYLE, borderlessStyle);
        //static const MARGINS shadow_state{ 1,1,1,1 };
       // ::DwmExtendFrameIntoClientArea(hwnd, &shadow_state);
        //::SetWindowPos(hwnd, nullptr, 110, 110, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE);
        SetWindowLongPtr(hwnd, GWL_EXSTYLE, WS_EX_APPWINDOW);
        SetWindowLongPtr(hwnd, GWL_STYLE, WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX  | WS_VISIBLE | WS_OVERLAPPED);
        // I tried this too, and same problem
        //SetWindowPos(hwnd, NULL, (GetDeviceCaps(GetDC(NULL), HORZRES) - W) / 2, (GetDeviceCaps(GetDC(NULL), VERTRES) - H) / 2, W, H, SWP_FRAMECHANGED | SWP_NOZORDER);
        //SetWindowPos(hwnd, NULL, (GetDeviceCaps(GetDC(NULL), HORZRES) - 100) / 2, (GetDeviceCaps(GetDC(NULL), VERTRES) - 100) / 2, 400, 400, NULL);
        SetWindowPos(hwnd, NULL, 0, 0, GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES), NULL);
        MSG msg;
        while (::GetMessageW(&msg, nullptr, 0, 0) == TRUE) {
            ::TranslateMessage(&msg);
            ::DispatchMessageW(&msg);
        }
    }
    LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        int wmId, wmEvent;
        PAINTSTRUCT ps;
        HDC hdc;
        switch (message)
        {
      
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
         
            EndPaint(hWnd, &ps);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return 0;
    }
    

    Here is the effect:
    User's image


1 additional answer

Sort by: Most helpful
  1. Tong Xu - MSFT 2,036 Reputation points Microsoft Vendor
    2024-05-03T04:23:57.5433333+00:00

    Hello, @Saito, Makoto (SIE)
    Welcome to Microsoft Q&A!
    Please note. Here is the English Q&A forum. Please use English to get the attention of the community and better help answer your questions.

    I want to use CreateWindowEx() on FSE to display a different window, is that possible?

    You can test my code. It should be possible and worked.

    // include the basic windows header files and the Direct3D header files
    #include <windows.h>
    #include <windowsx.h>
    #include <d3d11.h>
    #include <dxgi.h>
    #include<d3d10.h>
    #include<directxmath.h>
    // include the Direct3D Library file
    #pragma comment (lib, "d3d11.lib")
    #pragma comment(lib,"DXGI.lib")
    #pragma comment(lib,"D3D10.lib")
    // define the screen resolution
    #define SCREEN_WIDTH  800
    #define SCREEN_HEIGHT 600
    // global declarations
    IDXGISwapChain* swapchain;     
    // the pointer to the swap chain interface
    ID3D11Device* dev;                     // the pointer to our Direct3D device interface
    ID3D11DeviceContext* devcon;           // the pointer to our Direct3D device context
    ID3D11RenderTargetView* backbuffer;    // the pointer to our back buffer
    HWND hWnd;
    // function prototypes
    void InitD3D(HWND hWnd);    // sets up and initializes Direct3D
    void RenderFrame(void);     // renders a single frame
    void CleanD3D(void);        // closes Direct3D and releases memory
    // the WindowProc function prototype
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
    // the entry point for any Windows program
    int WINAPI WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nCmdShow)
    {
       ;
        WNDCLASSEX wc;
        ZeroMemory(&wc, sizeof(WNDCLASSEX));
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WindowProc;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        // wc.hbrBackground = (HBRUSH)COLOR_WINDOW;    // 不在需要
        wc.lpszClassName = L"WindowClass";
        RegisterClassEx(&wc);
        RECT wr = { 0, 0, NULL, NULL };
        //AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
         hWnd = CreateWindowEx(NULL,
            L"WindowClass",
            L"Our First Direct3D Program",
            NULL,
            300,
            300,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            NULL,
            NULL,
            hInstance,
            NULL);
        if (hWnd == NULL)
        {
            return 0;
        }
        ShowWindow(hWnd, nCmdShow);
       
        // set up and initialize Direct3D
        InitD3D(hWnd);
        CleanD3D();
        // enter the main loop:
     
        MSG msg;
        while (TRUE)
        {
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
                if (msg.message == WM_QUIT)
                    break;
            }
          
            
        }
      
        // clean up DirectX and COM
        return msg.wParam;
    }
    // this is the main message handler for the program
    LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        switch (message)
        {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        } break;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    // this function initializes and prepares Direct3D for use
    void InitD3D(HWND hWnd)
    {
        // create a struct to hold information about the swap chain
        DXGI_SWAP_CHAIN_DESC scd;
        // clear out the struct for use
        ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
        // fill the swap chain description struct
        scd.BufferCount = 1;                                    // one back buffer
        scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // use 32-bit color
        scd.BufferDesc.Width = NULL;                    // set the back buffer width
        scd.BufferDesc.Height = NULL;                  // set the back buffer height
        scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // how swap chain is to be used
        scd.OutputWindow = hWnd;                                // the window to be used
        scd.SampleDesc.Count = 4;                               // how many multisamples
        scd.Windowed = TRUE;                                    // windowed/full-screen mode
        scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;     // allow full-screen switching
        // create a device, device context and swap chain using the information in the scd struct
        D3D11CreateDeviceAndSwapChain(NULL,
            D3D_DRIVER_TYPE_HARDWARE,
            NULL,
            NULL,
            NULL,
            NULL,
            D3D11_SDK_VERSION,
            &scd,
            &swapchain,
            &dev,
            NULL,
            &devcon);
        // get the address of the back buffer
        ID3D11Texture2D* pBackBuffer;
        swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
        // use the back buffer address to create the render target
        dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
        pBackBuffer->Release();
        // set the render target as the back buffer
        devcon->OMSetRenderTargets(1, &backbuffer, NULL);
        // Set the viewport
        D3D11_VIEWPORT viewport;
        ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
        viewport.TopLeftX = 0;
        viewport.TopLeftY = 0;
        viewport.Width = NULL;
        viewport.Height = NULL;
        devcon->RSSetViewports(1, &viewport);
    }
    // this is the function used to render a single frame
    void RenderFrame(void)
    {
        float clearColor[4] = { 0.0f, 1.0f, 0.2f, 1.0f };
        // clear the back buffer to a deep blue
        devcon->ClearRenderTargetView(backbuffer, clearColor);
        // do 3D rendering on the back buffer here
        // switch the back buffer and the front buffer
        swapchain->Present(0, 0);
    }
    // this is the function that cleans up Direct3D and COM
    void CleanD3D(void)
    {
        ID3D11Texture2D* pTex = NULL;
        swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)pTex);
        HRESULT ree= swapchain->SetFullscreenState(TRUE, nullptr);    // switch to windowed mode
        if (ree != S_OK)
        {
            MessageBoxW(hWnd,L"error",L"error",NULL);
        }
        // close and release all existing COM objects
        swapchain->Release();
        backbuffer->Release();
        dev->Release();
        devcon->Release();
    }
    

    Because I worked on a Terminal Server, I cannot verify it worked:
    DXGI_ERROR_NOT_CURRENTLY_AVAILABLE if the action failed. When this error is returned, your application can continue to run in windowed mode and try to switch to full-screen mode later. There are many reasons why a windowed-mode swap chain cannot switch to full-screen mode. Here are some examples.

    • The application is running over Terminal Server.
    • The output window is occluded.
    • The output window does not have keyboard focus.
    • Another application is already in full-screen mode.

    I'd like to specify the WS_OVERLAPPED if possible, but is that possible?

    No, You certainly understand the definition of windowing and fullscreen. Using WS_OVERLAPPEDWINDOW is contradictory.

    Thank you.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.