
import os
import select
import signal
import struct
import subprocess
import sys

import gtk
import vte

class CallWindow:
    def __init__(self):
        self.window = gtk.Window()
        self.window.set_title('Call-o-Matic')
        self.window.set_border_width(2)
        self.table = gtk.Table(3, 2, True)
        self.table.set_col_spacings(2)
        self.table.set_row_spacings(2)

        def make_terminal():
            term = vte.Terminal()
            term.set_size_request(400, 220)
            term.set_font_from_string('Mono 6')
            term.set_color_foreground(gtk.gdk.Color(0xffff, 0xffff, 0xffff))
            return term

        self.a_call_term = make_terminal()
        self.a_cm_term = make_terminal()
        self.a_se_term = make_terminal()

        self.b_call_term = make_terminal()
        self.b_cm_term = make_terminal()
        self.b_se_term = make_terminal()

        o = gtk.SHRINK | gtk.EXPAND | gtk.FILL

        self.table.attach(self.a_call_term, 0, 1, 0, 1, o, o)
        self.table.attach(self.a_cm_term,   0, 1, 1, 2, o, o)
        self.table.attach(self.a_se_term,   0, 1, 2, 3, o, o)
        self.table.attach(self.b_call_term, 1, 2, 0, 1, o, o)
        self.table.attach(self.b_cm_term,   1, 2, 1, 2, o, o)
        self.table.attach(self.b_se_term,   1, 2, 2, 3, o, o)

        self.window.add(self.table)
        self.window.show_all()


    def run(self):
        def pipe():
            read_fd, write_fd = os.pipe()
            read_fh = os.fdopen(read_fd, 'r')
            write_fh = os.fdopen(write_fd, 'w')
            return read_fh, write_fh

        def red(s):
            return '\x1b[31m%s\x1b[0m' % s

        def tee(in_fh, out_fhs):
            while True:
                select.select([in_fh], [], [])
                line = in_fh.readline()

                if line == '':
                    break

                for out_fh in out_fhs:
                    print >>out_fh, line,
                    out_fh.flush()

        def _exec(term, cmd, dir=None, env=None, logfile=None):
            # Workaround for Terminal.forkpty(). The pipe is used to
            # synchronise the parent and the child, so that the subprocess is
            # not executed before the pty is attached. Removing either of the
            # ctl_w.close() invocations seems to break it.

            pid, pty = os.forkpty()
            crl_r, ctl_w = pipe()

            if pid > 0:
                term.set_pty(pty)
                ctl_w.write('\0')
                ctl_w.close()
                return

            ctl_w.close()
            crl_r.read(1)

            if logfile:
                log_fh = file(logfile, 'w')
                p = subprocess.Popen(cmd, env=env, cwd=dir,
                    stdout=subprocess.PIPE)
                tee(p.stdout, [sys.stdout, log_fh])
            else:
                p = subprocess.Popen(cmd, env=env, cwd=dir)

            try:
                code = p.wait()
            except KeyboardInterrupt:
                print red('interrupted')
                sys.exit(0)

            if code >= 0:
                print red('>>> exited with code %d' % code)
            else:
                print red('>>> killed with signal %d' % (-code))

            sys.exit(0)

        def launch_bus():
            s = subprocess.Popen(['dbus-launch', '--binary-syntax'],
                stdout=subprocess.PIPE).communicate()[0]
            addr, misc = s.split('\0', 1)
            pid, xid = struct.unpack('@ii', misc)
            return addr, pid

        def merge(a, b):
            c = a.copy()
            c.update(b)
            return c

        a_bus_addr, self.a_bus_pid = launch_bus()
        a_env = merge(os.environ, {'DBUS_SESSION_BUS_ADDRESS': a_bus_addr})

        b_bus_addr, self.b_bus_pid = launch_bus()
        b_env = merge(os.environ, {'DBUS_SESSION_BUS_ADDRESS': b_bus_addr})

        call_dir = '/home/daf/collabora/telepathy-python/examples'
        a_call_cmd = ['python', 'call.py', 'account.colltest1@ccu',
            'colltest2@collabora.co.uk']
        b_call_cmd = ['python', 'call.py', 'account.colltest2@ccu']

        cm_cmd = ['telepathy-gabble']
        cm_env = {
            'GABBLE_DEBUG': 'all',
            'LM_DEBUG': 'net',
            }
        a_cm_env = merge(cm_env, a_env)
        b_cm_env = merge(cm_env, b_env)

        se_cmd = ['telepathy-stream-engine']
        se_env = {
            'STREAM_ENGINE_PERSIST': '1',
            'FS_TRANSMITTER': 'nice',
            'FS_AUDIO_SRC': 'pulsesrc',
            'FS_VIDEO_SRC': 'videotestsrc',
            'FS_AUDIO_SINK': 'pulsesink',
            'FS_VIDEO_SINK': 'xvimagesink',
            }
        a_se_env = merge(se_env, a_env)
        b_se_env = merge(se_env, b_env)

        _exec(self.a_call_term, a_call_cmd, dir=call_dir, env=a_env)
        _exec(self.a_cm_term, cm_cmd, env=a_cm_env, logfile='a-gabble.log')
        _exec(self.a_se_term, se_cmd, env=a_se_env,
            logfile='a-stream-engine.log')

        _exec(self.b_call_term, b_call_cmd, dir=call_dir, env=b_env)
        _exec(self.b_cm_term, cm_cmd, env=b_cm_env, logfile='b-gabble.log')
        _exec(self.b_se_term, se_cmd, env=b_se_env,
            logfile='b-stream-engine.log')

    def stop(self):
        os.kill(self.a_bus_pid, signal.SIGTERM)
        os.kill(self.b_bus_pid, signal.SIGTERM)

def main():
    cw = CallWindow()
    cw.window.connect('destroy', gtk.main_quit)
    cw.window.maximize()
    cw.run()

    try:
        gtk.main()
    except KeyboardInterrupt:
        pass

    cw.stop()

if __name__ == '__main__':
    main()

