Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <fcall.h>
#include <plumb.h>
#include "dat.h"
#include "fns.h"
#include <ctype.h>

char    Ebadwr[]                = "bad rectangle in wctl request";
char    Ewalloc[]               = "window allocation failed in wctl request";

/* >= Top are disallowed if mouse button is pressed */
enum
{
        New,
        Resize,
        Move,
        Scroll,
        Noscroll,
        Set,
        Top,
        Bottom,
        Current,
        Hide,
        Unhide,
        Delete,
};

static char *cmds[] = {
        [New]   = "new",
        [Resize]        = "resize",
        [Move]  = "move",
        [Scroll]        = "scroll",
        [Noscroll]      = "noscroll",
        [Set]           = "set",
        [Top]   = "top",
        [Bottom]        = "bottom",
        [Current]       = "current",
        [Hide]  = "hide",
        [Unhide]        = "unhide",
        [Delete]        = "delete",
        nil
};

enum
{
        Cd,
        Deltax,
        Deltay,
        Hidden,
        Id,
        Maxx,
        Maxy,
        Minx,
        Miny,
        PID,
        R,
        Scrolling,
        Noscrolling,
};

static char *params[] = {
        [Cd]                            = "-cd",
        [Deltax]                        = "-dx",
        [Deltay]                        = "-dy",
        [Hidden]                        = "-hide",
        [Id]                            = "-id",
        [Maxx]                  = "-maxx",
        [Maxy]                  = "-maxy",
        [Minx]                  = "-minx",
        [Miny]                  = "-miny",
        [PID]                           = "-pid",
        [R]                             = "-r",
        [Scrolling]                     = "-scroll",
        [Noscrolling]           = "-noscroll",
        nil
};

/*
 * Check that newly created window will be of manageable size
 */
int
goodrect(Rectangle r)
{
        if(!eqrect(canonrect(r), r))
                return 0;
        if(Dx(r)<100 || Dy(r)<3*font->height)
                return 0;
        /* must have some screen and border visible so we can move it out of the way */
        if(Dx(r) >= Dx(screen->r) && Dy(r) >= Dy(screen->r))
                return 0;
        /* reasonable sizes only please */
        if(Dx(r) > BIG*Dx(screen->r))
                return 0;
        if(Dy(r) > BIG*Dx(screen->r))
                return 0;
        return 1;
}

static
int
word(char **sp, char *tab[])
{
        char *s, *t;
        int i;

        s = *sp;
        while(isspace(*s))
                s++;
        t = s;
        while(*s!='\0' && !isspace(*s))
                s++;
        for(i=0; tab[i]!=nil; i++)
                if(strncmp(tab[i], t, strlen(tab[i])) == 0){
                        *sp = s;
                        return i;
        }
        return -1;
}

int
set(int sign, int neg, int abs, int pos)
{
        if(sign < 0)
                return neg;
        if(sign > 0)
                return pos;
        return abs;
}

Rectangle
newrect(void)
{
        static int i = 0;
        int minx, miny, dx, dy;

        dx = min(600, Dx(screen->r) - 2*Borderwidth);
        dy = min(400, Dy(screen->r) - 2*Borderwidth);
        minx = 32 + 16*i;
        miny = 32 + 16*i;
        i++;
        i %= 10;

        return Rect(minx, miny, minx+dx, miny+dy);
}

void
shift(int *minp, int *maxp, int min, int max)
{
        if(*minp < min){
                *maxp += min-*minp;
                *minp = min;
        }
        if(*maxp > max){
                *minp += max-*maxp;
                *maxp = max;
        }
}

Rectangle
rectonscreen(Rectangle r)
{
        shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x);
        shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y);
        return r;
}

/* permit square brackets, in the manner of %R */
int
riostrtol(char *s, char **t)
{
        int n;

        while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='['))
                s++;
        if(*s == '[')
                s++;
        n = strtol(s, t, 10);
        if(*t != s)
                while((*t)[0] == ']')
                        (*t)++;
        return n;
}


int
parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err)
{
        int cmd, param, xy, sign;
        char *t;

        *pidp = 0;
        *hiddenp = 0;
        *scrollingp = scrolling;
        *cdp = nil;
        cmd = word(&s, cmds);
        if(cmd < 0){
                strcpy(err, "unrecognized wctl command");
                return -1;
        }
        if(cmd == New)
                r = newrect();

        strcpy(err, "missing or bad wctl parameter");
        while((param = word(&s, params)) >= 0){
                switch(param){  /* special cases */
                case Hidden:
                        *hiddenp = 1;
                        continue;
                case Scrolling:
                        *scrollingp = 1;
                        continue;
                case Noscrolling:
                        *scrollingp = 0;
                        continue;
                case R:
                        r.min.x = riostrtol(s, &t);
                        if(t == s)
                                return -1;
                        s = t;
                        r.min.y = riostrtol(s, &t);
                        if(t == s)
                                return -1;
                        s = t;
                        r.max.x = riostrtol(s, &t);
                        if(t == s)
                                return -1;
                        s = t;
                        r.max.y = riostrtol(s, &t);
                        if(t == s)
                                return -1;
                        s = t;
                        continue;
                }
                while(isspace(*s))
                        s++;
                if(param == Cd){
                        *cdp = s;
                        while(*s && !isspace(*s))
                                s++;
                        if(*s != '\0')
                                *s++ = '\0';
                        continue;
                }
                sign = 0;
                if(*s == '-'){
                        sign = -1;
                        s++;
                }else if(*s == '+'){
                        sign = +1;
                        s++;
                }
                if(!isdigit(*s))
                        return -1;
                xy = riostrtol(s, &s);
                switch(param){
                case -1:
                        strcpy(err, "unrecognized wctl parameter");
                        return -1;
                case Minx:
                        r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy);
                        break;
                case Miny:
                        r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy);
                        break;
                case Maxx:
                        r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy);
                        break;
                case Maxy:
                        r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy);
                        break;
                case Deltax:
                        r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy);
                        break;
                case Deltay:
                        r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy);
                        break;
                case Id:
                        if(idp != nil)
                                *idp = xy;
                        break;
                case PID:
                        if(pidp != nil)
                                *pidp = xy;
                        break;
                }
        }

        *rp = rectonscreen(rectaddpt(r, screen->r.min));

        while(isspace(*s))
                s++;
        if(cmd!=New && *s!='\0'){
                strcpy(err, "extraneous text in wctl message");
                return -1;
        }

        if(argp)
                *argp = s;

        return cmd;
}

int
wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err)
{
        char **argv;
        Image *i;

        if(!goodrect(rect)){
                strcpy(err, Ebadwr);
                return -1;
        }
        argv = emalloc(4*sizeof(char*));
        argv[0] = "rc";
        argv[1] = "-c";
        while(isspace(*arg))
                arg++;
        if(*arg == '\0'){
                argv[1] = "-i";
                argv[2] = nil;
        }else{
                argv[2] = arg;
                argv[3] = nil;
        }
        if(hideit)
                i = allocimage(display, rect, screen->chan, 0, DWhite);
        else
                i = allocwindow(wscreen, rect, Refbackup, DWhite);
        if(i == nil){
                strcpy(err, Ewalloc);
                return -1;
        }
        border(i, rect, Selborder, red, ZP);

        new(i, hideit, scrollit, pid, dir, "/bin/rc", argv);

        free(argv);     /* when new() returns, argv and args have been copied */
        return 1;
}

int
writewctl(Xfid *x, char *err)
{
        int cnt, cmd, j, id, hideit, scrollit, pid;
        Image *i;
        char *arg, *dir;
        Rectangle rect;
        Window *w;

        w = x->f->w;
        cnt = x->count;
        x->data[cnt] = '\0';
        id = 0;

        rect = rectsubpt(w->screenr, screen->r.min);
        cmd = parsewctl(&arg, rect, &rect, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
        if(cmd < 0)
                return -1;

        if(mouse->buttons!=0 && cmd>=Top){
                strcpy(err, "action disallowed when mouse active");
                return -1;
        }

        if(id != 0){
                for(j=0; j<nwindow; j++)
                        if(window[j]->id == id)
                                break;
                if(j == nwindow){
                        strcpy(err, "no such window id");
                        return -1;
                }
                w = window[j];
                if(w->deleted || w->i==nil){
                        strcpy(err, "window deleted");
                        return -1;
                }
        }

        switch(cmd){
        case New:
                return wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
        case Set:
                if(pid > 0)
                        wsetpid(w, pid, 0);
                return 1;
        case Move:
                rect = Rect(rect.min.x, rect.min.y, rect.min.x+Dx(w->screenr), rect.min.y+Dy(w->screenr));
                rect = rectonscreen(rect);
                /* fall through */
        case Resize:
                if(!goodrect(rect)){
                        strcpy(err, Ebadwr);
                        return -1;
                }
                if(eqrect(rect, w->screenr))
                        return 1;
                i = allocwindow(wscreen, rect, Refbackup, DWhite);
                if(i == nil){
                        strcpy(err, Ewalloc);
                        return -1;
                }
                border(i, rect, Selborder, red, ZP);
                wsendctlmesg(w, Reshaped, i->r, i);
                return 1;
        case Scroll:
                w->scrolling = 1;
                wshow(w, w->nr);
                wsendctlmesg(w, Wakeup, ZR, nil);
                return 1;
        case Noscroll:
                w->scrolling = 0;
                wsendctlmesg(w, Wakeup, ZR, nil);
                return 1;
        case Top:
                wtopme(w);
                return 1;
        case Bottom:
                wbottomme(w);
                return 1;
        case Current:
                wcurrent(w);
                return 1;
        case Hide:
                switch(whide(w)){
                case -1:
                        strcpy(err, "window already hidden");
                        return -1;
                case 0:
                        strcpy(err, "hide failed");
                        return -1;
                default:
                        break;
                }
                return 1;
        case Unhide:
                for(j=0; j<nhidden; j++)
                        if(hidden[j] == w)
                                break;
                if(j == nhidden){
                        strcpy(err, "window not hidden");
                        return -1;
                }
                if(wunhide(j) == 0){
                        strcpy(err, "hide failed");
                        return -1;
                }
                return 1;
        case Delete:
                wsendctlmesg(w, Deleted, ZR, nil);
                return 1;
        }
        strcpy(err, "invalid wctl message");
        return -1;
}

void
wctlthread(void *v)
{
        char *buf, *arg, *dir;
        int cmd, id, pid, hideit, scrollit;
        Rectangle rect;
        char err[ERRMAX];
        Channel *c;

        c = v;

        threadsetname("WCTLTHREAD");

        for(;;){
                buf = recvp(c);
                cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err);

                switch(cmd){
                case New:
                        wctlnew(rect, arg, pid, hideit, scrollit, dir, err);
                }
                free(buf);
        }
}

void
wctlproc(void *v)
{
        char *buf;
        int n, eofs;
        Channel *c;

        threadsetname("WCTLPROC");
        c = v;

        eofs = 0;
        for(;;){
                buf = emalloc(messagesize);
                n = read(wctlfd, buf, messagesize-1);   /* room for \0 */
                if(n < 0)
                        break;
                if(n == 0){
                        if(++eofs > 20)
                                break;
                        continue;
                }
                eofs = 0;

                buf[n] = '\0';
                sendp(c, buf);
        }
}