How can I get screenshot from all displays with X11?

838 views Asked by At

I was working on writing a screenshot thing, and found this excellent topic for Mac: How can I get screenshot from all displays on MAC?

I was wondering if anyone has the equivalent for x11 library? To get all the monitors and then screenshot them all?

I had found this topic: https://stackoverflow.com/a/5293559/1828637

But the code linked from there is not as easy to follow for a novice like me.

Will RootWindow(3) get the area of all the monitors combined? Then I can go through and get the monitors dimensions then XGetImage those sections on the return of RootWindow?

I had come across this topic: How do take a screenshot correctly with xlib? But I'm not sure if it has multi-monitor support. I do this in ctypes so I cant test that code easily without going through the grueling task of writing it first. So I was wondering if this is correct or how would I modify it to handle multi mon please?

Edit

The poster there shared his code, it is seen here: https://github.com/Lalaland/ScreenCap/blob/master/src/screenCapturerImpl.cpp#L96 but it's complicated and I don't understand it. It uses functions like XFixesGetCursorImage which I can't find in the documentation, and I don't see how the multi monitors work there. Author of that topic warned he doesn't remember the code and it may not work with modern Linux.

1

There are 1 answers

1
Brandon D On

This is not a perfect answer to the question, but the following code could be modified to get a very fast version of your desired end result: https://github.com/Clodo76/vr-desktop-mirror/blob/master/DesktopCapture/main.cpp

The DesktopCapturePlugin_Initialize method converts all the displays into objects:

UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API DesktopCapturePlugin_Initialize()
{   
    DesksClean();

    g_needReinit = 0;


    IDXGIFactory1* factory;
    CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));

    IDXGIAdapter1* adapter;
    for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
    {
        IDXGIOutput* output;
        for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); j++)
        {
            DXGI_OUTPUT_DESC outputDesc;
            output->GetDesc(&outputDesc);

            MONITORINFOEX monitorInfo;
            monitorInfo.cbSize = sizeof(MONITORINFOEX);
            GetMonitorInfo(outputDesc.Monitor, &monitorInfo);

            // Maybe in future add a function to identify the primary monitor.
            //if (monitorInfo.dwFlags == MONITORINFOF_PRIMARY)
            {
                int iDesk = DeskAdd();

                g_desks[iDesk].g_width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;                  
                g_desks[iDesk].g_height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;

                auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
                IDXGIOutput1* output1;
                output1 = reinterpret_cast<IDXGIOutput1*>(output);
                output1->DuplicateOutput(device, &g_desks[iDesk].g_deskDupl);
            }

            output->Release();
        }
        adapter->Release();
    }

    factory->Release();
}

Then the OnRenderEvent method copies a frame from the display into a texture (provided by unity in this case):

void UNITY_INTERFACE_API OnRenderEvent(int eventId)
{
    for (int iDesk = 0; iDesk < g_nDesks; iDesk++)
    {
        if (g_desks[iDesk].g_deskDupl == nullptr || g_desks[iDesk].g_texture == nullptr)
        {
            g_needReinit++;
            return;
        }

        IDXGIResource* resource = nullptr;

        const UINT timeout = 0; // ms
        HRESULT resultAcquire = g_desks[iDesk].g_deskDupl->AcquireNextFrame(timeout, &g_desks[iDesk].g_frameInfo, &resource);
        if (resultAcquire != S_OK)
        {
            g_needReinit++;
            return;
        }

        g_desks[iDesk].g_isPointerVisible = (g_desks[iDesk].g_frameInfo.PointerPosition.Visible == TRUE);
        g_desks[iDesk].g_pointerX = g_desks[iDesk].g_frameInfo.PointerPosition.Position.x;
        g_desks[iDesk].g_pointerY = g_desks[iDesk].g_frameInfo.PointerPosition.Position.y;

        ID3D11Texture2D* texture;
        HRESULT resultQuery = resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
        resource->Release();

        if (resultQuery != S_OK)
        {
            g_needReinit++;
            return;
        }

        ID3D11DeviceContext* context;
        auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
        device->GetImmediateContext(&context);
        context->CopyResource(g_desks[iDesk].g_texture, texture);

        g_desks[iDesk].g_deskDupl->ReleaseFrame();
    }

    g_needReinit = 0;
}