linuxchromebookactive-windowcrostini

How can I detect the active window in Crostini?


I would like to programmatically detect the active window, and its meta data -- like app name and title -- in Chromebook's Linux Crostini. Does anyone know how this can reliably be done?

FWIW, I've already tried the following three methods in python that, when combined, provide a full solution for Fedora, Ubuntu, Arch, Manjaro, Mint, etc. But none of them work on Crostini.

def getAppAndTitle():
    title = ''
    app = ''

    try:
        # METHOD 1: Try to get window id info from _NET_ACTIVE_WINDOW
        actives = []
        try:
            actives = re.findall('0x([0-9a-fA-F]+)', subprocess.check_output("xprop -root _NET_ACTIVE_WINDOW", shell=True).decode('utf-8'))
        except:
            pass
        if len(actives) > 0:
            active_id = int(actives[0], 16)
            if active_id != 0:
                window_strs = ''
                # Get the list of all window names & ids
                try:
                    window_strs = subprocess.check_output("wmctrl -lx", shell=True).decode('utf-8')
                except:
                    pass
                for window_str in window_strs.splitlines():
                    acceptable = re.search('^0x([0-9a-fA-F]+)\s+\S+\s+(\S+)\s+\S+\s+(.*)$', window_str)
                    if acceptable:
                        acceptable_id = int(acceptable.group(1), 16)
                        if acceptable_id == active_id:
                            app, title = acceptable.group(2).strip(), acceptable.group(3).strip()
    except Exception as e1:
        pass

    try:
        # METHOD 2: Try to get window info from Wnck
        if not (title and app):
            screen = None
            try:
                screen = Wnck.Screen.get_default()
            except Exception as e2:
                pass
            if screen:
                screen.force_update()
                active_window = screen.get_active_window()
                if active_window:
                    title = active_window.get_name().strip()
                    app = active_window.get_application().get_name().strip()
    except Exception as e1:
        pass

    try:
        # METHOD 3: Designed for Wayland
        if not (title and app):
            app_command = ("gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval global.get_window_actors\(\)[`gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval global.get_window_actors\(\).findIndex\(a\=\>a.meta_window.has_focus\(\)===true\) | cut -d\"'\" -f 2`].get_meta_window\(\).get_wm_class\(\)")
            app_process = subprocess.Popen(app_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            app_retval = app_process.stdout.read()
            app_retcode = app_process.wait()
            app_tuple_string = app_retval.decode('utf-8').strip()
            # We now have a string that looks like:
            # (true, "\"Qur'an App"\")
            if app_tuple_string:
                app_tuple_string = app_tuple_string.replace("(true", "(True")
                app_tuple_string = app_tuple_string.replace("(false", "(False")
                # We now have a string that looks like:
                # (True, "\"Qur'an App"\")
                app_tuple = ast.literal_eval(app_tuple_string)
                if len(app_tuple) > 1:
                    app = app_tuple[1]
                    # We now have a string that looks like:
                    # "Qur'an App"
                    if app and isinstance(app, str) and (type(app) is str):
                        app = app.strip()
                        if len(app) > 2:
                            if (app[0:1] == '"' and app[-1] == '"') or (app[0:1] == "'" and app[-1] == "'"):
                                app = ast.literal_eval(app).strip()
                                # We now have a string that looks like:
                                # Qur'an App
                                title_command = ("gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval global.get_window_actors\(\)[`gdbus call -e -d org.gnome.Shell -o /org/gnome/Shell -m org.gnome.Shell.Eval global.get_window_actors\(\).findIndex\(a\=\>a.meta_window.has_focus\(\)===true\) | cut -d\"'\" -f 2`].get_meta_window\(\).get_title\(\)")
                                title_process = subprocess.Popen(title_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                                title_retval = title_process.stdout.read()
                                title_retcode = title_process.wait()
                                title_tuple_string = title_retval.decode('utf-8').strip()
                                if title_tuple_string:
                                    title_tuple_string = title_tuple_string.replace("(true", "(True")
                                    title_tuple_string = title_tuple_string.replace("(false", "(False")
                                    title_tuple = ast.literal_eval(title_tuple_string)
                                    if len(title_tuple) > 1:
                                        title = title_tuple[1]
                                        if title and isinstance(title, str) and (type(title) is str):
                                            title = title.strip()
                                            if len(title) > 2:
                                                if (title[0:1] == '"' and title[-1] == '"') or (title[0:1] == "'" and title[-1] == "'"):
                                                    title = ast.literal_eval(title)
                                                    if "error:" in app.lower():
                                                        title = ''
                                                        app = ''
    except Exception as e1:
        title = ''
        app = ''
    return app, title

Any thoughts on other ways of doing this?


Solution

  • I've been able to accomplish this using the following two terminal commands.

    The first gets the tree of running windows.

    xwininfo -root -tree
    

    The second tells me which of those is the one with focus.

    xdpyinfo | grep focus