#+TITLE: Demonstration on how to switch windows with python
#+DATE: 2017-04-07 12:00:00 UTC
#+DESCRIPTION: Shows how to get all window and change focus sequentially, could be used to implement your own window switcher. 
#+FILETAGS: xcb:python:xorg
#+CATEGORY: python
#+SLUG: xcb-window-switching-example
#+THUMBNAIL: ../../../images/xcb/xcb-window-switcher.jpg
#+BEGIN_COMMENT
.. title: Demonstration on how to switch windows with python
.. slug: xcb-window-switching-example
.. date: 2017-04-07 12:00:00 UTC
.. tags: xcb, python, xorg
.. category: python
.. description: Shows how to get all window and change focus sequentially, could be used to implement your own window switcher. 
.. type: text
#+END_COMMENT


#+CAPTION: Window list output 
[[../../../images/xcb/xcb-window-switcher.jpg]]


I have been experimenting with writing my own launcher app similar to gnome do, one of the things i wanted was to 
switch windows by typing the name, so i wrote the below example to work out how to switch window.

It works with you xserver to request a list of windows and then send the event to change focus, i have only tested 
this in gnome how ever it will likely work with other desktops.

The main thing you need todo is to set input focus before raising the window, if you do not some window managers like 
gnome will just notify you instead of raising the window.


#+BEGIN_SRC python
#!/usr/bin/env python
import time
import struct

import xcb
from xcb import xproto

# standard xcb to get an xserver connection and root window
xserver = xcb.connect()
xclient = xserver.core
setup = xserver.get_setup()

canvas = setup.roots[0]
root_window = canvas.root

unsigned_int_32_max_size = (1 << 32) - 1


def get_atom(name):
    return xclient.InternAtom(
        0,
        len(name),
        name
    ).reply()


# setup a load of atoms as constants for later use
_NET_WM_STATE = get_atom('_NET_WM_STATE')
_NET_WM_NAME = get_atom('_NET_WM_NAME')
_NET_WM_WINDOW_TYPE = get_atom('_NET_WM_WINDOW_TYPE')
_NET_WM_WINDOW_TYPE_APPLICATION = get_atom('_NET_WM_WINDOW_TYPE_NORMAL')
_NET_CLIENT_LIST = get_atom('_NET_CLIENT_LIST')

# query _NET_CLIENT_LIST which contains a list of windows
clientlist_reply = xclient.GetProperty(
    False, root_window, _NET_CLIENT_LIST.atom,
    xcb.xproto.GetPropertyType.Any,
    0, unsigned_int_32_max_size).reply()

# unpack the reply into a list, xcb returns a list of ints
# in this case
window_list = list(struct.unpack(
    'I' * clientlist_reply.value_len, clientlist_reply.value.buf()))


for window in window_list:
    if window is 0:
        continue

    # get some of the useful properties
    # you can use xprop in a terminal to see whats available
    window_name = xclient.GetProperty(
        delete=False,
        window=window,
        property=xproto.Atom.WM_NAME,
        type=xproto.GetPropertyType.Any,
        long_offset=0,
        long_length=unsigned_int_32_max_size).reply()

    window_class = xclient.GetProperty(
        delete=False,
        window=window,
        property=xproto.Atom.WM_CLASS,
        type=xproto.GetPropertyType.Any,
        long_offset=0,
        long_length=unsigned_int_32_max_size).reply()

    window_type = xclient.GetProperty(
        delete=False,
        window=window,
        property=_NET_WM_WINDOW_TYPE.atom,
        type=xproto.GetPropertyType.Any,
        long_offset=0,
        long_length=1).reply()

    window_net_name = xclient.GetProperty(
        delete=False,
        window=window,
        property=_NET_WM_NAME.atom,
        type=xproto.Atom.Any,
        long_offset=0,
        long_length=unsigned_int_32_max_size).reply()

    # This does the focus, set the window to have input focus
    # then raise the window with configure window
    xclient.SetInputFocus(
        xcb.xproto.InputFocus.Parent,
        window,
        xcb.xproto.Time.CurrentTime)

    xclient.ConfigureWindow(
        window,
        xproto.ConfigWindow.StackMode,
        [xproto.StackMode.Above])

    xserver.flush()
    time.sleep(0.25)

    window_class = ''.join([chr(c) for c in window_class.value])
    window_name = ''.join([chr(c) for c in window_name.value])
    window_net_name = ''.join([chr(c) for c in window_net_name.value])

    print('-------')
    print('window id = %s' % window)
    print('window name = %s' % window_name)
    print('window net name = %s' % window_net_name)
    print('window class = %s' % window_class)
#+END_SRC

