#!/usr/bin/python3
#
# Run given command; switch override_redirect off on one new X11 window.
# Also change the title to $_NET_WM_NAME (or blank), and set a fixed size hint.
# Useful for working around https://github.com/QubesOS/qubes-issues/issues/2311
#
# Example: x11-unoverride-redirect dmenu -i

import os
import struct
import subprocess
import sys
import xcffib
from xcffib import xproto
from xcffib.xproto import Atom, CW, PropMode

XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS = 18
XCB_ICCCM_SIZE_HINT_P_MIN_SIZE = 1 << 4
XCB_ICCCM_SIZE_HINT_P_MAX_SIZE = 1 << 5


def title_property():
    env = os.getenvb(b'_NET_WM_NAME', b'')
    return len(env), env

def fixed_size_property(geometry):
    hint = [0] * XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
    hint[0] = XCB_ICCCM_SIZE_HINT_P_MIN_SIZE | XCB_ICCCM_SIZE_HINT_P_MAX_SIZE
    hint[5], hint[6] = hint[7], hint[8] = geometry.width, geometry.height
    return XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS, struct.pack(
        f'=I{XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS - 2}iI', *hint)

def adjust_window(window, core):
    yield core.ChangeWindowAttributesChecked(window, CW.OverrideRedirect, [0])
    attributes_cookie = core.GetWindowAttributes(window)
    geometry_cookie = core.GetGeometry(window)
    yield core.ChangePropertyChecked(
        # pylint: disable=no-member,protected-access
        PropMode.Replace, window, Atom._NET_WM_NAME, Atom.UTF8_STRING, 8,
        *title_property())
    yield core.ChangePropertyChecked(
        PropMode.Replace, window, Atom.WM_NORMAL_HINTS, Atom.WM_SIZE_HINTS, 32,
        *fixed_size_property(geometry_cookie.reply()))
    if attributes_cookie.reply().map_state != xproto.MapState.Unmapped:
        yield core.UnmapWindowChecked(window)
        yield core.MapWindowChecked(window)

def main():
    connection = xcffib.connect()
    core = connection.core

    for s in ('_NET_WM_NAME', 'UTF8_STRING'):
        setattr(Atom, s, core.InternAtom(0, len(s), s).reply().atom)

    core.ChangeWindowAttributesChecked(
        connection.setup.roots[0].root, CW.EventMask,
        [xproto.EventMask.SubstructureNotify]
    ).check()

    with subprocess.Popen(sys.argv[1:]) as command:
        for ev in iter(connection.wait_for_event, None):
            if command.poll() is not None:  # command finished
                break
            if isinstance(ev, xproto.CreateNotifyEvent) \
               and ev.override_redirect:
                for cookie in list(adjust_window(ev.window, core)):
                    cookie.check()
                break
        connection.disconnect()

    status = command.returncode
    if status < 0:
        status = 128 - status
    return status


if __name__ == '__main__':
    sys.exit(main())
