Computing the visible area of a partly hidden window

command linescreenshotx11

I have to (scriptly) take screenshots of windows, some of which may be partially or fully hidden by other windows. Of course, in such a case the screenshot is useless, and I'd rather print a warning.

Unfortunately I'm not in a situation where I can use a Window Manager which prints even hidden or minimized windows — I'm stuck with an old KDE.

I can use e.g. xwininfo -id <windowid> | grep "Map State" to determine if the window IsViewable (which basically means standard screenshot tools won't complain), but I can't seem to figure out how to determine if the window is overlapped by other windows and how much it is (then I could print the warning if, say, less than 50 % of the window are visible).

(As I'm actually trying to workaround it because I don't believe it exists, of course I won't complain if anyone points me to an easy method to e.g. "virtually redraw" windows so that the screenshot is worth it…)

Best Answer

As far as I know (but I'm no X expert), visibility is only tracked through VisibilityNotify events, it isn't a state that you can query like IsViewable. You'd need to get KDE to spit it out, I don't know if it can do that.

You may be solve your problem by sending the window a VisibilityNotify(state=VisibilityUnobscured) event. I don't know if that's enough, you may also need to send a MapNotify event. Here's some untested Python code to send a VisibilityNotify event.

#! /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))
    display.sync()

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