How to take a screenshot of an totally obscured window

screenshotx11

I have a game running in the background, but I would like to take screenshots of the window which is not visible. Seems like all the screenshot utilities for X11 need the window to be visible. Is there a way to do this?

Best Answer

It's probably the fault of the game, not the fault of the screenshot utility. X11 sends applications a VisibilityNotify event to tell them that their window is fully visible, partially obscured or totally obscured. When the window is totally obscured, most applications don't bother updating their display, which saves resources. In other words, if nobody is there to see it, the tree doesn't fall.

I think that if you send the game window a VisibilityNotify event to pretend that it's visible, then you'll get your screenshot. You'll need to send the event after the window becomes obscured, since X11 itself will send its normal event at that time. Here's an untested script that sends a VisibilityNotify event, call it with the argument VisibilityPartiallyObscured or VisibilityUnobscured. I follow with a MapNotify event, I don't know if it's useful. You need Python and Python-xlib.

#! /usr/bin/env python
import re, sys, time
import Xlib.X, Xlib.XK, Xlib.display, Xlib.protocol

def parse_action(string):
    state = {
              '0': 0,
              '1': 1,
              '2': 2,
              'unobscured': 0,
              'partiallyobscured': 1,
              'fullyobscured': 2,
              'visibilityunobscured': 0,
              'visibilitypartiallyobscured': 1,
              'visibilityfullyobscured': 2,
             }[string.lower()]
    return state

def parse_window(display, arg):
    wid = int(arg, 0)
    return display.create_resource_object('window', wid)

def send_event(display, window, state):
    window.send_event(Xlib.protocol.event.VisibilityNotify(window=window,
                                                           state=state))
    window.send_event(Xlib.protocol.event.MapNotify(window=window,
                                                    event=window,
                                                    override=False))
    display.sync()

if __name__ == "__main__":
    display = Xlib.display.Display()
    send_event(display, parse_window(display, sys.argv[1]), parse_action(sys.argv[2]))
Related Question