ActivityWatch/aw-watcher-window

View on GitHub
aw_watcher_window/xprop.py

Summary

Maintainability
A
25 mins
Test Coverage
import sys
import subprocess
import re
import logging
from subprocess import PIPE
from typing import List

logger = logging.getLogger(__name__)

# req_version is 3.5 due to usage of subprocess.run
# It would be nice to be able to use 3.4 as well since it's still common as of May 2016
req_version = (3, 5)
cur_version = sys.version_info

if not cur_version >= req_version:
    logger.error("Your Python version is too old, 3.5 or higher is required.")
    exit(1)


def xprop_id(window_id) -> str:
    cmd = ["xprop"]
    cmd.append("-id")
    cmd.append(window_id)
    p = subprocess.run(cmd, stdout=PIPE)
    return str(p.stdout, "utf8")


def xprop_root() -> str:
    cmd = ["xprop"]
    cmd.append("-root")
    p = subprocess.run(cmd, stdout=PIPE)
    return str(p.stdout, "utf8")


def get_active_window_id():
    lines = xprop_root().split("\n")
    match="_NET_ACTIVE_WINDOW(WINDOW)"
    result = None
    for line in lines:
        if match in line:
            result = line
            break
    wid = "0x0"
    if result:
        wids = re.findall("0x[0-9a-f]*", result)
        if len(wids) > 0:
            wid = wids[0]
    return wid


def get_window_ids():
    lines = xprop_root().split("\n")
    client_list = next(filter(lambda x: "_NET_CLIENT_LIST(" in x, lines))
    window_ids = re.findall("0x[0-9a-f]*", client_list)
    return window_ids


def _extract_xprop_field(line):
    return "".join(line.split("=")[1:]).strip(" \n")


def get_xprop_field(fieldname, xprop_output):
    return list(map(_extract_xprop_field, re.findall(fieldname + ".*\n", xprop_output)))


def get_xprop_field_str(fieldname, xprop_output) -> str:
    field = None
    try:
        field = get_xprop_field(fieldname, xprop_output)[0].strip('"')
    except IndexError:
        pass
    if not field:
        field = "unknown"
    return field


def get_xprop_field_strlist(fieldname, xprop_output) -> List[str]:
    return [s.strip('"') for s in get_xprop_field(fieldname, xprop_output)]


def get_xprop_field_int(fieldname, xprop_output) -> int:
    field = None
    try:
        field = int(get_xprop_field(fieldname, xprop_output)[0])
    except IndexError:
        pass
    if not field:
        field = -1
    return field


def get_xprop_field_class(xprop_output) -> List[str]:
    classname: List[str] = []
    try:
        classname = [c.strip('", ') for c in get_xprop_field("WM_CLASS", xprop_output)[0].split(',')]
    except IndexError:
        pass
    if not classname:
        classname = ["unknown"]
    return classname


def get_window(wid, active_window=False):
    s = xprop_id(wid)
    window = {
        "id": wid,
        "active": active_window,
        "name": get_xprop_field_str("WM_NAME", s),
        "class": get_xprop_field_class(s),
        "desktop": get_xprop_field_int("WM_DESKTOP", s),
        "command": get_xprop_field("WM_COMMAND", s),
        "role": get_xprop_field_strlist("WM_WINDOW_ROLE", s),
        "pid": get_xprop_field_int("WM_PID", s),
    }

    return window


def get_windows(wids, active_window_id=None):
    return [get_window(wid, active_window=(wid == active_window_id)) for wid in wids]


if __name__ == "__main__":
    from time import sleep
    logging.basicConfig(level=logging.INFO)
    while True:
        sleep(1)
        print("Active window id: " + str(get_active_window_id()))