Search code examples
pythonscreenshotwindows-subsystem-for-linuxmonitor

Screenshotting the windows desktop when working through WSL


I'm primarily using Windows, where I run WSL2. So from a python script running in the subsystem, I would like to screenshot whatever is on windows monitor, as simple as such:

v1

import mss
import os

os.environ['DISPLAY'] = ':0'

with mss.mss() as sct:
    sct.shot()

This gives only gives "Segmentation fault" error and no image. So I tried to setup vcxsrv in Windows and I'm able to open stuff from my subsystem in Windows through the server, however I cant get it the other way around..

I just want access to the windows screen so I can screenshot it. Any help about how to access the monitor through wsl would be greatly appreciated, I can't find much on google..


Solution

  • The problem with your attempted solution is that the WSL/Linux Python's mss, as you've found, isn't able to capture the Windows desktop. Being the Linux version of MSS, it will only be able to communicate with Linux processes and protocols like X. Starting up VcXsrv might get you part of the way there, in that you might be able to capture X output, but you may need to be running the Python app from inside a terminal that is running in a X window.

    Regardless, you've said that your goal is to capture the entire Windows desktop, not just the X output in VcXsrv. To do that, you'll need to use a Windows process of some sort.

    But don't worry; using WSL's interop, you can still do that from inside WSL/Linux Python. We just need to call out to a Windows .exe of some sort.

    There are literally dozens of third-party apps that you could use in Windows to create a screenshot. But I prefer to use a solution that doesn't require any additional installation.

    So I resorted to PowerShell for this, since you can easily call powershell.exe and pass in a script from WSL. There are a number of examples here on Stack Overflow, but I ended up going slightly "lower tech" to try to simplify a bit. The code here is most similar to this solution, so refer to that if you want to expand on this.

    From WSL/Linux Python:

    import os
    os.system("""
        powershell.exe \"
            Add-Type -AssemblyName System.Windows.Forms
            [Windows.Forms.Sendkeys]::SendWait('+{Prtsc}')
            \$img = [Windows.Forms.Clipboard]::GetImage()
            \$img.Save(\\\"\$env:USERPROFILE\\Pictures\\Screenshots\\screenshot.jpg\\\", [Drawing.Imaging.ImageFormat]::Jpeg)\"
    """)
    

    That essentially sends the ShiftPrintScreen key chord to capture the current desktop to the clipboard, then saves the clipboard. It can get slightly hairy with the quoting, since you are essentially wrapping PowerShell inside a /bin/sh inside a Python script.

    Note that, even though you are in Linux Python, since it's the Windows PowerShell that we are calling, it takes the Windows path format (C:\Users\Username\Pictures...) rather than the Linux version (/mnt/c/Users/...).

    While I didn't have any timing issues with this, you may need to insert small delays. Again, refer to the existing answer for that. This solution is primarily to explain how to do it through WSL's Python using PowerShell.