Monday, March 29, 2010

X application startup script with wnck

It is common that several X applications are involved for one task. For example, web browser is used for searching and help document, Emacs is used to edit, and X termainal is used for testing. Here, script can be used to startup several X programs with one command typing or click.

But simple shell script can only startup applications. You must arrange the screen layout by hand. In fact the screen layout can be program with script too. Python + wnck can be used for that.

The script as follow is an example. It startup pidgin and gnome-terminal, minimize pidgin and maximize gnome-terminal vertically. With wnck, you can arrage the layout of your X applications arbitrarily.

#!/usr/bin/python

import gobject
import gtk
import wnck
import os
import time

def gtk_wait():
gobject.idle_add(gtk.main_quit)
gtk.main()

def get_wnck_apps_from_wins(wins):
apps = set()
for win in wins:
app = win.get_application()
apps.add(app)
return list(apps)

def screen_get_wnck_apps(s):
wins = s.get_windows()
return get_wnck_apps_from_wins(wins)

def screen_find_windows_by_pid(s, pid):
wins = []
all_wins = s.get_windows()
for win in all_wins:
app = win.get_application()
if app.get_pid() == pid:
wins.append(win)
return wins

def gtk_wait_timeout():
gtk_wait()
time.sleep(0.1)

def apply_timeout(func, args, timeout = 10, wait = gtk_wait_timeout):
start = time.time()
res = None
while not res:
res = apply(func, args)
now = time.time()
if now - start > timeout:
break
wait()
return res

def wait_child_proc():
try:
os.waitpid(-1, os.WNOHANG)
except OSError, err:
if err.errno == 10: # No child processes
pass

def pid_is_valid(pid):
return os.path.exists('/proc/%d' % (pid,))

def screen_find_windows_by_pid_timeout(s, pid):
while True:
wins = apply_timeout(screen_find_windows_by_pid, (s, pid), 0.5)
if wins:
break
wait_child_proc()
if not pid_is_valid(pid):
break
return wins

def init():
global screen
screen = wnck.screen_get_default()
gtk_wait()

class app(object):
def __init__(self, pid):
self.pid = pid
self.update()
def update(self):
self.wnck_wins = screen_find_windows_by_pid_timeout(screen, self.pid)
self.wnck_apps = get_wnck_apps_from_wins(self.wnck_wins)
def minimize(self):
for win in self.wnck_wins:
win.minimize()
def maximize(self):
for win in self.wnck_wins:
wt = win.get_window_type()
if wt == wnck.WINDOW_NORMAL:
win.maximize()
def maximize_vertically(self):
for win in self.wnck_wins:
wt = win.get_window_type()
if wt == wnck.WINDOW_NORMAL:
win.maximize_vertically()
def close(self):
for win in self.wnck_wins:
win.close(0)

class app_info(object):
def __init__(self, start_argv, app_name):
object.__init__(self)
self.start_argv = start_argv
self.app_name = app_name
def get_instances(self):
pids = []
wnck_apps = screen_get_wnck_apps(screen)
for wnck_app in wnck_apps:
if wnck_app.get_name() == self.app_name:
pids.append(wnck_app.get_pid())
if pids:
return [app(pid) for pid in pids]
else:
return [self.new_instance()]
def new_instance(self):
argv = self.start_argv[:]
argv.insert(0, 'nohup')
pid = os.spawnvp(os.P_NOWAIT, 'nohup', argv)
return app(pid)

def start_apps():
pidgin_info = app_info(['pidgin'], 'Pidgin')
terminal_info = app_info(['gnome-terminal'], 'Terminal')

os.system('rm -f nohup.out ~/nohup.out')

apps = pidgin_info.get_instances()
apps = pidgin_info.get_instances()
for app in apps:
app.close()

apps = terminal_info.get_instances()
for app in apps:
app.maximize_vertically()

init()
start_apps()

No comments: