/*
 * seven terminal emulator and more
 *
 * big diagram
 *
 * client -     pane      input
 *               ^v         |
 * shared -     script      |
 *                ^         v
 * server -     emulate <- pty
 *
 */


#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#ifdef __APPLE__
#include <util.h>
#else
#include <pty.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <utmp.h>
#include <wchar.h>
#include <wctype.h>

#include <popt.h>
#include <X11/Xlocale.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>

#include "defaults.h"
#include "emulator.h"
#include "keysym2ucs.h"
#include "multigraph.h"
#include "pane.h"
#include "panic.h"
#include "print_event.h"
#include "proto.h"
#include "pty-outfifo.h"
#include "seven_client.h"
#include "unicode.h"
#include "unix.h"
#include "x11.h"
#include "xatoms.h"
#include "selections.h"
#include "keys.h"
#include "mod.h"
#include "debug.h"

static void queue(unsigned what);
void sevenprog_1(struct svc_req *rqstp, register SVCXPRT *transp);

static bool is_visible;
static bool is_drawing;
static int draw_pending;

static int num_pending = 0;
static int num_final = 0;

static struct pane *pane;
static sc_t *sc;
static struct emulate *emulate;
static char *svc_socket;
static SVCXPRT *svc;
static void *dummy_ptr;
static char *arg_option;
static struct terminfo_data terminfo_data;

// IO Channels

static int x_channel_watch = -1;


#define PEND_QCALLBACK 1
#define PEND_TIORESIZE 2
#define PEND_XFLUSH 4
#define PEND_XEXPOSE (PEND_XFLUSH | 8)

static unsigned pending;

static fifo_t in_fifo = FIFO_BLANK;

static int opt_nofork;
static int opt_detached;
static int opt_dumpopts;
static int opt_version;

static struct selection_info selection;

static void do_redraw(void);

#ifndef NDEBUG
bool opt_debug;
#endif


static const struct poptOption options[] = {
        { NULL, 0, POPT_ARG_INCLUDE_TABLE, (void *)x11_options, 0, "X Windows options", NULL },
        { "option", 'o', POPT_ARG_STRING, &arg_option, 'o', "set any configuration option", "name:value" },
        { "exec", 'e', POPT_ARG_STRING, NULL, 'e', "Command to run", "command [ args  ..]"},
        { "detached", 'd', POPT_ARG_NONE, &opt_detached, 0, "Do not connect to display, start in detatched mode.", NULL},
        { "nofork", 0, POPT_ARG_NONE, &opt_nofork, 0, "Stay in the foreground, quit with the child programs exit status.", NULL},
        { "dumpopts", 0, POPT_ARG_NONE, &opt_dumpopts, 0, "Dump options to stdout", NULL},
        { "version" , 0, POPT_ARG_NONE, &opt_version, 0, "print version number and exit", NULL},
#ifndef NDEBUG
        { "debug" , 0, POPT_ARG_NONE, &opt_debug, 0, "print out debugging info", NULL},
#endif

        POPT_AUTOHELP
        POPT_TABLEEND
};

static gboolean
event_finalizer(void *null)
{
        num_final++;
        if(pending & PEND_TIORESIZE)
                ioctl(pty->fd, TIOCSWINSZ, &pty->winsize);
        if((pending & PEND_XEXPOSE) == PEND_XEXPOSE ) {draw_pending++; do_redraw(); }
//        if((pending & PEND_XEXPOSE) == PEND_XEXPOSE ) {
//                if(!is_visible ||  is_drawing) {
//                        draw_pending = true;
//                } else {
//                        XEvent ev;
//                        memset(&ev, 0, sizeof(ev));
//                        ev.type = ClientMessage;
//                        ev.xclient.data.l[0]=XA_SEVEN_PING;
//                        ev.xany.window = win.w;
//                        ev.xany.display = conn->dpy;
//                        ev.xclient.format = 32;
//                        ev.xclient.message_type = XA_ATOM;
//
//                        pane_draw(pane);
//                        XSendEvent(conn->dpy, win.w, false, 0, &ev);
//                        is_drawing = true;
//                }
//        }
        if((pending & PEND_XFLUSH) && conn) XFlush(conn->dpy);
        pending = 0;
        //if((num_final % 10) == 0)
        //        fprintf(stderr, "p/f = %i/%i  %lf\n", num_pending, num_final, (double)num_pending/(double)num_final);
        return false;
}


static void
add_timeval(struct timeval *tv, int num)
{
        tv->tv_usec += num;
        tv->tv_sec += tv->tv_usec / 1000000;
        tv->tv_usec %= 1000000;
}

static void
do_redraw(void)
{
    if(!conn) return;
    is_drawing = false;
    if(!is_visible ||  is_drawing) {
        draw_pending++;
    } else {
        pane_draw(pane, true, true);
        is_drawing = true;
        draw_pending = 0;
        XFlush(conn->dpy);

    }
}


static void
queue(unsigned what)
{
        assert(what);
        if(!pending) {
                g_idle_add(event_finalizer,NULL);

        }
        pending |= what;
}

static void
resize_event(int w, int h)
{
        int fw, fh;
        int cwidth, cheight;
        if (w == win.width && h == win.height) return;
        win.width = w;
        win.height = h;
        get_charcell_size(&fw, &fh);
        if(DB_force80x25) {
                cheight = 25;
                cwidth = 80;
        } else {
                cheight = h / fh;
                cwidth = w / fw;
        }
        pty->winsize.ws_row = cheight;
        pty->winsize.ws_col = cwidth;
        pty->winsize.ws_xpixel = w;
        pty->winsize.ws_ypixel = h;

        pane_setup(pane, cwidth, cheight);
        emulate_set_size(emulate, cwidth, cheight);
        queue(PEND_TIORESIZE | PEND_XEXPOSE);
        //ioctl(pty->fd, TIOCSWINSZ, &pty->winsize);
        do_redraw();
        if (!XSyncValueIsZero(win.syncValue)) {
                XSyncSetCounter(conn->dpy, win.syncCounter,win.syncValue);
                XSyncIntToValue(&win.syncValue,0);
        }

}



//TODO
//fix overflow bugs.
static void
handle_keypress(XKeyEvent kev)
{
        KeySym keysym;
        char *ptr = NULL;
        static char mg_buf[64];
        static char *mg_state = NULL;
        char buf[41];
        int n = XLookupString(&kev,buf,40,&keysym,NULL);
        if(n > 0) { buf[n] = '\0'; ptr = buf; }
        //if(!mg_state && keysym == XK_5 && (kev.state & ControlMask)) {
        if(xevent_match_key((XEvent *)&kev, &DK_keyDumpStats)) {
                print_emulator_state(stderr, emulate);
                return;
        }
        //if(!mg_state && keysym == XK_Insert && (kev.state & ShiftMask)) {
        if(xevent_match_key((XEvent *)&kev, &DK_keyInsertSelection)) {
                selection_get(&selection, conn->dpy, win.w, XA_PRIMARY, XA_TEXT, kev.time);
                queue(PEND_XFLUSH);
                return;
        }
        if(!mg_state && keysym == XK_Insert && (kev.state & ControlMask)) {
                selection_get(&selection, conn->dpy, win.w, XA_PRIMARY, XA_TARGETS, kev.time);
                queue(PEND_XFLUSH);
                return;
        }
        //if(!mg_state && keysym == XK_2 && (kev.state & ControlMask)) {
        if(xevent_match_key((XEvent *)&kev, &DK_keyMultigraphStart)) {
                XSetWindowBorder(conn->dpy, win.w, DC_digraphBorderColor->pixel);
                queue(PEND_XFLUSH);
                mg_state = mg_buf;
                memset(mg_buf, 0, sizeof(mg_buf));
                return;
        }
        if(mg_state) {
                struct mgraph *mg;
                strcpy(mg_state, buf);
                mg_state += n;
                mg = bsearch(mg_buf, mgraphs, NUM_MGRAPHS, sizeof(mgraph_t), compare_mgraph_exact);
                if(mg) {
                        send_wchar( pty, mg->c);
                        mg_state = NULL;
                        XSetWindowBorder(conn->dpy, win.w, DC_borderColor->pixel);
                        queue(PEND_XFLUSH);
                        return;
                }
                mg = bsearch(mg_buf, mgraphs, NUM_MGRAPHS, sizeof(mgraph_t), compare_mgraph_substr);
                if(!mg) {
                        mg_state = NULL;
                        XSetWindowBorder(conn->dpy, win.w, DC_borderColor->pixel);
                        queue(PEND_XFLUSH);
                }
                else
                        return;
        }
        if(xevent_match_key((XEvent *)&kev, &DK_keyDumpStats) ) {
                char *s;
                asprintf(&s, "#num_pending %u\n#num_final %u\n#out_fifo %u %u\n#in_fifo %u %u\n", num_pending, num_final, -1, -1, FIFO_LEN(in_fifo), in_fifo.rb.size);
                stuff_buf(pty, s, strlen(s));
                free(s);
                return;
        }

        char *ptr_ = process_key(keysym,emulate,&terminfo_data);
        if(ptr_) ptr = ptr_;

        if(ptr) {
                for(;*ptr;ptr++) send_wchar(pty, *ptr);
        } else {
                unsigned cp = keysym2ucs(keysym);
                if (cp != -1) send_wchar(pty, cp);
        }
}


static void
update_timestamp(Display *disp, Window w, Time time)
{
        unsigned long t = (unsigned long)time;
        XChangeProperty(disp, w, XA__NET_WM_USER_TIME, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&t, 1);
}

static gboolean
on_x_event(GIOChannel *source, GIOCondition event, void *null)
{
        XEvent ev;
        if(!IS_ATTACHED())
                return true;

        while(XPending(conn->dpy)) {
                XNextEvent(conn->dpy, &ev);
                //print_event(conn->dpy, ev);
                switch(ev.type) {
                case ClientMessage:
                        if((Atom)ev.xclient.data.l[0]==XA_WM_DELETE_WINDOW) {
                                g_main_loop_quit(mainloop);
                                return true;
                        }
                        if((Atom)ev.xclient.data.l[0]==XA__NET_WM_PING) {
                                ev.xclient.window = DefaultRootWindow(conn->dpy);
                                XSendEvent(conn->dpy, DefaultRootWindow(conn->dpy), False,SubstructureRedirectMask | SubstructureNotifyMask, &ev);
                        } else if((Atom)ev.xclient.data.l[0]==XA__NET_WM_SYNC_REQUEST) {
                                printf("_NET_WM_SYNC_REQUEST: %8.8lx%8.8lx\n", (unsigned long)ev.xclient.data.l[3],(unsigned long)ev.xclient.data.l[2]);
                                XSyncIntsToValue((&win.syncValue), ev.xclient.data.l[2], ev.xclient.data.l[3]);
                        } else if((Atom)ev.xclient.data.l[0]==XA_SEVEN_PING) {
                                is_drawing = false;
                                queue(PEND_XEXPOSE);
                                //if(draw_pending)
                                //        do_redraw();
                                //return NULL;
                        } else
                        print_event( ev);
                        break;
                case Expose:
                        pane_toucharea(pane, ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height);
                        draw_pending++;
                        //do_redraw();
                        //queue(PEND_XEXPOSE);
                        break;
                case KeyPress:
                        update_timestamp(conn->dpy, win.w, ev.xkey.time);
                        pane_scrollback(pane, 0);
                        handle_keypress(ev.xkey);
                        break;
                case ButtonPress:
                        update_timestamp(conn->dpy, win.w, ev.xbutton.time);
                        if(ev.xbutton.button == 4) {
                                pane_scrollback(pane, DI_wheelScrollAmount);
                                draw_pending++;
                                //do_redraw();
                        } else if(ev.xbutton.button == 5) {
                                pane_scrollback(pane, -DI_wheelScrollAmount);
                                draw_pending++;
                                //do_redraw();
                        } else print_event( ev);
                        //queue(PEND_XEXPOSE);
                        break;
                case MappingNotify:
                        XRefreshKeyboardMapping(&(ev.xmapping));
                        queue(PEND_XFLUSH);
                        break;
                case ConfigureNotify:
                        resize_event(ev.xconfigure.width, ev.xconfigure.height);
                        break;
                case UnmapNotify:
                        is_visible = false;
                case MapNotify:
                        break;
                case PropertyNotify:
                case SelectionNotify:
                        {
                                void *buf = NULL;
                                int len = 0;
                                //print_event( ev);
                                if(selection_process(&selection, &ev, &buf, &len)) {
                                        if(selection.type == None)
                                                break;
                                        printf("got selection: %s\n", XGetAtomName(conn->dpy, selection.type));
                                        if(selection.type != XA_ATOM)
                                        stuff_buf(pty, buf, len);
                                        else {
                                                for(int i = 0; i < len; i++)
                                                        printf("%s\n", XGetAtomName(conn->dpy, ((Atom *)buf)[i]));;
                                                }
                                        if(buf)
                                                XFree(buf);

                                }
                                break;
                        }
                case VisibilityNotify:
                        switch (ev.xvisibility.state) {
                        case VisibilityUnobscured:
                        case VisibilityPartiallyObscured:
                                is_visible = true;
                                break;
                        case VisibilityFullyObscured:
                                is_visible = false;
                                break;
                        default:
                                break;
                        }
                        break;
                default:
                        print_event( ev);
                }
        }
        num_pending++;
        //pane_draw(pane, true, false);
       // XFlush(conn->dpy);
        if(draw_pending)
                do_redraw();
        XFlush(conn->dpy);
        return true;
}


#define INCOMING_BUFFER 4096

static gboolean
on_incoming_data(GIOChannel *source, GIOCondition event, void *_null)
{
        unsigned char buf[INCOMING_BUFFER];
        wchar_t wc;
        int i,r;

        int fd = g_io_channel_unix_get_fd(source);

        while(1) {
                errno = 0;
                ssize_t ret = read(fd, buf, INCOMING_BUFFER);
                if(!ret || errno == EIO) {
                        close(fd);
                        g_main_loop_quit(mainloop);
                        return true;
                }
                if(ret == -1) {
                        if(errno == EWOULDBLOCK)
                                break;
                        panicnof("read: %s",pty->name);
                }

                //fprintf(stderr, "on_incoming_data %u %i\n", FIFO_LEN(in_fifo), ret);
                fifo_append(&in_fifo, buf, ret);
                do {
                        char *p = FIFO_PTR(in_fifo);
                        int len = FIFO_LEN(in_fifo);
                        for(i = 0; i < len && !(p[i]&0x80); i++);
                        if (i) {
                                emulate_data(emulate, p, i, sc, true);
                                //draw_pending++;
                                queue(PEND_XEXPOSE);
                                FIFO_POP(in_fifo, i);
                                len -= i;
                                p += i;
                        }
                        if(len) {
                                r = utf8_mbtowc(&wc,p,len);
                                if(!r) continue;
                                if(r < 0) {
                                        r = -r;
                                        if(opt_debug) {
                                                fprintf(stderr, "invalid bytes:");
                                                for (int x = 0; x < r; x++)
                                                        fprintf(stderr, " %02x", (unsigned)((unsigned char *)FIFO_PTR(in_fifo))[x]);
                                                fprintf(stderr, "\n");
                                        }
                                } else {
                                        emulate_data(emulate, &wc, 1, sc, false);
                                        draw_pending++;
                                }
                                FIFO_POP(in_fifo, r);

                        } else
                                r = 0;
                } while(!FIFO_EMPTY(in_fifo) && (i || r));
        }
        num_pending++;
        //queue(PEND_XEXPOSE);
        //if(draw_pending)
        //        do_redraw();
        return true;
}


static gboolean
on_svc(GIOChannel *source, GIOCondition event, void *null)
{
        if(opt_debug)
                fprintf(stderr, "on_svc(%p,%i,%p)\n", source, event, null);
        svc_run();
        xprt_register(svc);
        return true;
}


static void
done(void)
{
        unlink(svc_socket);
}

struct seven_info *
sproto_getinfo_1_svc(struct svc_req *rqstp)
{
	static struct seven_info result;
        static struct x11_info xi;

        result.connections.connections_val = &xi;

        if(conn) {
        result.connections.connections_len = 1;
        xi.xwindow = win.w;
        xi.display = DisplayString(conn->dpy);
        xi.xauthority = (getenv("XAUTHORITY"));
        xi.server_vendor = ServerVendor(conn->dpy);
        } else {
        result.connections.connections_len = 0;
        }
        result.name = pty->sty;
        result.title = ("title");
        result.klass = "seven";

        svc_exit();
	return &result;
}

char **
sproto_attach_1_svc(struct x11_info x11, struct svc_req *rq)
{
        static char *buf = "";
        if(!conn) {
                x11_attach(&x11);
                pane_attach(pane,conn);
                XMapWindow(conn->dpy, win.w);
                XFlush(conn->dpy);
                GIOChannel *x_channel = g_io_channel_unix_new(ConnectionNumber(conn->dpy));
                x_channel_watch = g_io_add_watch(x_channel, G_IO_IN | G_IO_HUP | G_IO_ERR, on_x_event, NULL);
                g_io_channel_unref(x_channel);

        }

        svc_exit();
        return &buf;
}

void *
sproto_detach_1_svc(struct svc_req *rq)
{
        if(conn) {
                if (x_channel_watch != -1) {
                        g_source_remove(x_channel_watch);
                        x_channel_watch = -1;
                }
                pane_detach(pane);
                x11_detach();
        }

        svc_exit();
        return &dummy_ptr;
}

void *
sproto_switchscreen_1_svc(int nscreen, struct svc_req *rq)
{

        if(opt_debug)
                fprintf(stderr, "switchscreen(%i)\n", nscreen);
        x11_switchscreen(nscreen);

	XSelectInput(conn->dpy,win.w,KeyPressMask|ExposureMask|StructureNotifyMask|VisibilityChangeMask|ButtonPressMask|PropertyChangeMask);
        XMapWindow(conn->dpy,win.w);
        pane_touchall(pane);
        queue(PEND_XFLUSH);
        svc_exit();
        return &dummy_ptr;
}

static void
app_color(rb_t *rb, struct color *c)
{
        char buf[32];
        sprintf(buf, "#%2.2x%2.2x%2.2x", c->red / 256, c->green / 256, c->blue / 256);
        rbs_append(rb, buf);
}

static void
rbs_printf(rb_t *rb, char *fmt, ...)
{
        char *s;
        va_list ap;
        va_start(ap, fmt);
        int ret = vasprintf(&s, fmt, ap);
        if(ret == -1)
                panicno("rbs_printf");
        rbs_append(rb, s);
        free(s);
        va_end(ap);
}

static void
app_wchar(rb_t *rb, wchar_t wc)
{
        switch(wc) {
        case '&':
                rbs_append(rb, "&amp;");
                return;
        case '<':
                rbs_append(rb, "&lt;");
                return;
        case '>':
                rbs_append(rb, "&gt;");
                return;
        case 0:
                rbs_append(rb, " ");
                return;
        }
        if(wc > 0x7f)
                rbs_printf(rb, "&#%u;", wc);
        else
                RB_APPEND(char, rb, wc);
}

static void
app_attribute(rb_t *rb, struct cell c, bool start)
{
        if(c.fgcolor == DC_foreground && c.bgcolor == DC_background)
                return;
        if(start) {
        rbs_append(rb, "<span style=\"background: ");
        if(c.bgcolor)
                app_color(rb, c.bgcolor);
        else
                app_color(rb, DC_background);
        rbs_append(rb, "; color: ");

        if(c.fgcolor)
                app_color(rb, c.fgcolor);
        else
                app_color(rb, DC_foreground);
        rbs_append(rb, "\">");
        } else {
                rbs_append(rb,"</span>");

        }
}

char **
sproto_textshot_1_svc(bool_t get_all, struct svc_req *rqstp)
{
        static rb_t rb = RB_BLANK;
        static char *rb_ptr = NULL;
        RB_CLEAR(rb);

        rbs_append(&rb, "<pre style=\"background: ");
        app_color(&rb, DC_background);
        rbs_append(&rb, "; color: ");
        app_color(&rb, DC_foreground);
        rbs_printf(&rb, "\" width=%u>\n", emulate->cols);
        struct cell oc;
        memset(&oc, 0, sizeof(struct cell));
        //int height = get_all ? RBL(struct line, &p->lines) : pane->height;
        int start = get_all ? (pane->height - RBL(struct line, &pane->lines)) : 0;

        for(int j = start; j < pane->height; j++) {
                for(int i = 0; i < pane->width; i++) {
                        struct cell *c = cell(pane,i,j, false);
                        if(!c) {
                                if(oc.fgcolor)
                                                app_attribute(&rb, oc, false);
                                rbs_append(&rb, " ");
                                oc.fgcolor = NULL;
                        } else {
                                struct cell nc = *c;
                                nc.ch = 0;
                                if(memcmp(&nc, &oc, sizeof(struct cell))) {
                                        if(!(i == 0 && j == 0))
                                                app_attribute(&rb, oc, false);
                                        app_attribute(&rb, nc, true);
                                }
                                //app_attribute(&rb, *c, true);
                                app_wchar(&rb, c->ch);
                                //app_attribute(&rb, *c, false);
                                oc = nc;
                        }
                }
                rbs_append(&rb, "\n");
        }


        app_attribute(&rb, oc, false);
        rbs_append(&rb,"</pre>");


        rb_stringize(&rb);
        svc_exit();
        rb_ptr = RB_PTR(&rb);
	return (char **)&rb_ptr;
}


static const char * const d_dargv[] =  { "/bin/sh", NULL };


int
main(int argc, const char *argv[])
{
        char *buf;
        int rc;

        setlocale(LC_ALL, "");

        poptContext popt = poptGetContext(PACKAGE, argc, argv, options, POPT_CONTEXT_POSIXMEHARDER);


        char **dargv = (char **)d_dargv;

        while ((rc = poptGetNextOpt(popt)) > 0) {
            switch(rc) {
            case 'o':
                {
                    char *opt = strchr(arg_option, ':');
                    if(!opt)
                        fprintf(stderr, "Invalid option string: %s\nIt should be of form 'key:value'.\n", arg_option);
                    *opt++ = '\0';
                    defaults_option(arg_option, opt);
                    break;
                }
                // -e is tricky since it is not a normal option.
            case 'e':
                {
                    const char **av = argv;
                    int ac = argc;
                    while (!(!strcmp(*av, "-e") || !strcmp(*av, "--exec")) ) {
                        int l = strlen(*av);
                        if(l > 1 && (*av)[0] == '-' && (*av)[1] != '-' && strchr((*av) + 1, 'e'))  {
                            fprintf(stderr, "-e may not occur in a compound option.\n");
                            goto opt_error;
                        }
                        av++; ac--;
                    }
                    if(!ac)
                        goto opt_error;
                    av++;
                    dargv = malloc(sizeof(char *) * (ac + 1));
                    memcpy(dargv, av, sizeof(char *) * ac);
                    dargv[ac] = NULL;
                    goto done;
                }
            }
        }
        if (rc < -1) {
opt_error:
                /* an error occurred during option processing */
                fprintf(stderr, "%s: %s\n",
                        poptBadOption(popt, POPT_BADOPTION_NOALIAS),
                        poptStrerror(rc));
                exit(1);
        }

        if(poptPeekArg(popt))  {
                const char **av = argv;
                int ac = argc;
                while (*av != poptPeekArg(popt)) {
                        av++; ac--;
                }
                poptFreeContext(popt);
                do_client_call( ac, av);
                return 0;
        }
done:
        poptFreeContext(popt);
        if(opt_version) {
                puts(PACKAGE_STRING);
                return 0;
        }


        // store arguments so they can be passed to screens
        x11_init(argc, (char **)argv);

        if(!opt_detached) {
                struct x11_info x;
                x11_get_info(&x);
                x11_attach(&x);
        } else {
                defaults_finalize();
        }

        get_terminfo_data(&terminfo_data);
        unsetenv("LINES");
        unsetenv("COLUMNS");
        unsetenv("TERMCAP");
        asprintf(&buf, "TERM=%s", D_terminalToEmulate);
        putenv(buf);

        if(!opt_detached) {
                asprintf(&buf, "WINDOWID=%lu", win.w);
                putenv(buf);
        }

        char *shell;
        if (dargv == (char **)d_dargv && (shell = getenv("SHELL"))) {
                dargv = malloc(sizeof(char *) * 2);
                dargv[0] = shell;
                dargv[1] = NULL;
        }
        char **cs = dargv;
        if(opt_debug) {
                while(*cs) fprintf(stderr, "%s ", *(cs++));
                fprintf(stderr, "\n");
        }
        if(opt_dumpopts) {
                defaults_print();
                return 0;
        }

        if(terminfo_data.kbs[0] == '\0' || terminfo_data.kbs[1] != '\0') {
                fprintf(stderr, "Backspace is not single character code. Is your terminfo corrupt?\n");
                pty = spawn_p(dargv, 0177);
        } else
                pty = spawn_p(dargv, terminfo_data.kbs[0]);
        if(dargv != (char **)d_dargv)
                free(dargv);

        chdir("/");  // to not hold mountpoints open

        emulate = emulate_create();
        emulate->pty = pty;
        if(opt_debug) {
                fprintf(stderr, "STY=%s\n", pty->sty);
        }

        sc = script_new();
        pane = pane_create(sc);


        mainloop = g_main_loop_new(NULL,true);

        if(!opt_detached) {
                pane_attach(pane,conn);
                XSelectInput(conn->dpy,win.w,KeyPressMask|ExposureMask|StructureNotifyMask|VisibilityChangeMask|ButtonPressMask|PropertyChangeMask);
        } else {
                pane_setup(pane,80,25);
                emulate_set_size(emulate, 80,25);
                pty->winsize.ws_row = 80;
                pty->winsize.ws_col = 25;
                pty->winsize.ws_xpixel = 80;
                pty->winsize.ws_ypixel = 25;
        }


        // set the pty to be in non-blocking mode
        int fl = fcntl(pty->fd, F_GETFL);
        if(fl == -1) panicno("fcntl");
        fl |= O_NONBLOCK;
        fl = fcntl(pty->fd, F_SETFL, fl);
        if(fl == -1) panicno("fcntl");

        if(!opt_detached) {
                win.width++;
                resize_event(win.width - 1,win.height);
                XSync(conn->dpy, False);
                on_x_event(NULL,0,NULL);
                GIOChannel *x_channel = g_io_channel_unix_new(ConnectionNumber(conn->dpy));
                x_channel_watch = g_io_add_watch_full(x_channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR, on_x_event, NULL, NULL);
                g_io_channel_unref(x_channel);
        }


        if(!pty_channel) pty_channel = g_io_channel_unix_new(pty->fd);
        g_io_add_watch(pty_channel, G_IO_IN | G_IO_HUP | G_IO_ERR, on_incoming_data, NULL);

        mkdir("/tmp/seven", 01777);
        asprintf(&buf, "/tmp/seven/%u", getuid());
        mkdir(buf, 0700);
        free(buf);
        asprintf(&svc_socket, "/tmp/seven/%u/%s", getuid(), pty->sty);
        svc = svcunix_create(RPC_ANYSOCK,0,0,svc_socket);
	if (svc == NULL) {
		fprintf (stderr, "%s", "cannot create unix service.\n");
		exit(1);
	}
        atexit(done);
        if(!svc_register(svc, SEVENPROG, SEVENVERS, sevenprog_1, 0)) {
		fprintf (stderr, "%s", "unable to register (SEVENPROG, SEVENVERS, unix).\n");
		exit(1);
	}


        GIOChannel *svc_channel = g_io_channel_unix_new(svc->xp_sock);
        g_io_add_watch(svc_channel, G_IO_IN | G_IO_HUP | G_IO_ERR, on_svc, NULL);
        g_io_channel_unref(svc_channel);


        if(!opt_detached)
                XMapWindow(conn->dpy, win.w);
        g_main_loop_run(mainloop);

	x11_close();
	return 0;
}


