ref: f75e1bb49e5b3a979d716b05b1d815f8b5a9590c
dir: /wind.c/
#include "inc.h"
Window *bottomwin, *topwin;
Window *windows[1000]; // TMP
int nwindows;
Window *hidden[1000];
int nhidden;
Window *focused, *cursorwin;
static void winthread(void *arg);
static void
wlistpushback(Window *w)
{
w->higher = bottomwin;
if(bottomwin) bottomwin->lower = w;
w->lower = nil;
if(topwin == nil) topwin = w;
bottomwin = w;
}
static void
wlistpushfront(Window *w)
{
w->lower = topwin;
if(topwin) topwin->higher = w;
w->higher = nil;
if(bottomwin == nil) bottomwin = w;
topwin = w;
}
static void
wlistremove(Window *w)
{
if(w->lower)
w->lower->higher = w->higher;
else
bottomwin = w->higher;
if(w->higher)
w->higher->lower = w->lower;
else
topwin = w->lower;
w->higher = nil;
w->lower = nil;
}
void
wcalcrects(Window *w)
{
if(w->noborder)
w->contrect = w->img->r;
else
w->contrect = insetrect(w->img->r, Borderwidth);
Rectangle r = insetrect(w->contrect, 1);
w->scrollr = r;
w->scrollr.max.x = w->scrollr.min.x + 12;
w->textr = r;
w->textr.min.x = w->scrollr.max.x + 4;
}
void
wdecor(Window *w)
{
if(w->noborder)
return;
int c = w->holdmode ?
w == focused ? TITLEHOLD : LTITLEHOLD :
w == focused ? TITLE : LTITLE;
border(w->img, w->img->r, Borderwidth, colors[c], ZP);
}
void
wsetcolors(Window *w)
{
int c = w->holdmode ?
w == focused ? HOLDTEXT : PALEHOLDTEXT :
w == focused ? TEXT : PALETEXT;
w->text.cols[TEXT] = colors[c];
}
static void
wsetsize(Window *w, Rectangle r)
{
Rectangle hr;
if(w->img)
freeimage(w->img);
if(w->hidden){
hr = rectaddpt(r, subpt(screen->r.max, r.min));
w->img = allocwindow(wscreen, hr, Refbackup, DNofill);
originwindow(w->img, r.min, hr.min);
}else
w->img = allocwindow(wscreen, r, Refbackup, DNofill);
wcalcrects(w);
draw(w->img, w->img->r, colors[BACK], nil, ZP);
xinit(&w->text, w->textr, w->scrollr, font, w->img, colors);
}
static int id = 1;
Window*
wcreate(Rectangle r, bool hidden, bool scrolling)
{
Window *w;
w = emalloc(sizeof(Window));
incref(w);
w->id = id++;
w->notefd = -1;
wsetlabel(w, "<unnamed>");
w->dir = estrdup(startdir);
w->hidden = hidden;
w->scrolling = scrolling;
wsetsize(w, r);
wdecor(w);
wlistpushfront(w);
// TMP - make dynamic
windows[nwindows++] = w;
w->mc.c = chancreate(sizeof(Mouse), 16);
w->mc.image = w->img;
w->gone = chancreate(sizeof(int), 0);
w->kbd = chancreate(sizeof(char*), 16);
w->ctl = chancreate(sizeof(int), 0);
w->conswrite = chancreate(sizeof(Channel**), 0);
w->consread = chancreate(sizeof(Channel**), 0);
w->kbdread = chancreate(sizeof(Channel**), 0);
w->mouseread = chancreate(sizeof(Channel**), 0);
w->wctlread = chancreate(sizeof(Channel**), 0);
w->complete = chancreate(sizeof(Completion*), 0);
threadcreate(winthread, w, mainstacksize);
wsetname(w);
return w;
}
/* called from winthread when it exits */
static void
wfree(Window *w)
{
if(w->notefd >= 0)
close(w->notefd);
xclear(&w->text);
chanclose(w->mc.c);
chanclose(w->gone);
chanclose(w->kbd);
chanclose(w->ctl);
chanclose(w->conswrite);
chanclose(w->consread);
chanclose(w->kbdread);
chanclose(w->mouseread);
chanclose(w->wctlread);
chanclose(w->complete);
free(w->label);
free(w);
}
static void
wclose(Window *w)
{
int i;
if(w->deleted)
return;
w->deleted = TRUE;
assert(w != focused); /* this must be done elsewhere */
wlistremove(w);
for(i = 0; i < nwindows; i++)
if(windows[i] == w){
nwindows--;
memmove(&windows[i], &windows[i+1], (nwindows-i)*sizeof(Window*));
break;
}
if(w->img){
/* rio does this, not sure if useful */
originwindow(w->img, w->img->r.min, screen->r.max);
freeimage(w->img);
}
w->img = nil;
flushimage(display, 1);
}
int
wrelease(Window *w)
{
int i;
i = decref(w);
if(i > 0)
return 0;
if(i < 0)
panic("negative ref count");
wunfocus(w);
wclose(w);
wsendmsg(w, Closed);
return 1;
}
void
wsendmsg(Window *w, int type)
{
assert(w->threadname != threadgetname());
sendul(w->ctl, type);
}
Window*
wfind(int id)
{
int i;
for(i = 0; i < nwindows; i++)
if(windows[i]->id == id)
return windows[i];
return nil;
}
Window*
wpointto(Point pt)
{
Window *w;
for(w = topwin; w; w = w->lower)
if(!w->hidden && ptinrect(pt, w->img->r))
return w;
return nil;
}
void
wsetcursor(Window *w)
{
if(w == cursorwin)
setcursornormal(w->holdmode ? &whitearrow : w->cursorp);
}
void
wsetlabel(Window *w, char *label)
{
free(w->label);
w->label = estrdup(label);
}
void
wsetname(Window *w)
{
int i, n;
char err[ERRMAX];
n = snprint(w->name, sizeof(w->name)-2, "%s.%d.%d", w->noborder ? "noborder" : "window", w->id, w->namecount++);
for(i='A'; i<='Z'; i++){
if(nameimage(w->img, w->name, 1) > 0)
return;
errstr(err, sizeof err);
if(strcmp(err, "image name in use") != 0)
break;
w->name[n] = i;
w->name[n+1] = 0;
}
w->name[0] = 0;
fprint(2, "lola: setname failed: %s\n", err);
}
void
wsetpid(Window *w, int pid, int dolabel)
{
char buf[32];
int ofd;
ofd = w->notefd;
if(pid <= 0)
w->notefd = -1;
else{
if(dolabel){
snprint(buf, sizeof(buf), "rc %lud", (ulong)pid);
wsetlabel(w, buf);
}
snprint(buf, sizeof(buf), "/proc/%lud/notepg", (ulong)pid);
w->notefd = open(buf, OWRITE|OCEXEC);
}
if(ofd >= 0)
close(ofd);
}
void
wdelete(Window *w)
{
wunfocus(w);
wsendmsg(w, Deleted);
}
void
wrepaint(Window *w)
{
wsetcolors(w);
wdecor(w);
if(!w->mouseopen)
xredraw(&w->text);
}
/* restore window order after reshaping has disturbed it */
void
worder(void)
{
Window *w;
for(w = bottomwin; w; w = w->higher)
if(!w->hidden)
topwindow(w->img);
}
void
wresize(Window *w, Rectangle r)
{
wsetsize(w, r);
if(w != topwin && !w->hidden)
worder();
wsendmsg(w, Resized);
}
void
wmove(Window *w, Point pos)
{
Point delta;
/* BUG: originwindow causes the old window rect to be drawn onto the new one
* with backing store of allocscreen
* happens in _freeimage1(*winp); in libdraw/init.c:gengetwindow
* where *winp has the old rectangle
*
* We don't care if we're handling resizing ourselves though */
if(w->mouseopen){
delta = subpt(pos, w->img->r.min);
wresize(w, rectaddpt(w->img->r, delta));
}else{
originwindow(w->img, pos, w->hidden ? screen->r.max : pos);
if(w != topwin && !w->hidden)
worder();
wcalcrects(w);
xsetrects(&w->text, w->textr, w->scrollr);
wsendmsg(w, Moved);
}
}
void
wraise(Window *w)
{
wlistremove(w);
wlistpushfront(w);
topwindow(w->img);
flushimage(display, 1);
}
void
wlower(Window *w)
{
wlistremove(w);
wlistpushback(w);
bottomwindow(w->img);
flushimage(display, 1);
}
void
wfocus(Window *w)
{
Window *prev;
if(w == focused)
return;
prev = focused;
focused = w;
sendp(wintap, w);
/* TODO a bit ugly the repetition,
* but this might not stay anyways */
if(prev){
prev->wctlready = TRUE;
wrepaint(prev);
wsendmsg(prev, Wakeup);
}
if(focused){
focused->wctlready = TRUE;
wrepaint(focused);
wsendmsg(focused, Wakeup);
}
}
void
wunfocus(Window *w)
{
if(w == focused)
wfocus(nil);
}
int
whide(Window *w)
{
if(w->hidden)
return -1;
incref(w);
wunfocus(w);
w->hidden = TRUE;
w->wctlready = TRUE;
originwindow(w->img, w->img->r.min, screen->r.max);
wsendmsg(w, Wakeup);
wrelease(w);
return 1;
}
int
wunhide(Window *w)
{
if(!w->hidden)
return -1;
incref(w);
w->hidden = FALSE;
w->wctlready = TRUE;
originwindow(w->img, w->img->r.min, w->img->r.min);
wraise(w);
wfocus(w);
wrelease(w);
return 1;
}
void
wsethold(Window *w, int hold)
{
int prev;
if(hold)
prev = w->holdmode++;
else
prev = --w->holdmode;
if(prev == 0){
wsetcursor(w);
wrepaint(w);
}
}
void
wmovemouse(Window *w, Point pt)
{
// TODO? rio also checks menuing and such
if(w == focused && wpointto(mctl->xy) == w)
moveto(mctl, pt);
}
/*
* Need to do this in a separate proc because if process we're interrupting
* is dying and trying to print tombstone, kernel is blocked holding p->debug lock.
*/
static void
interruptproc(void *v)
{
int *notefd;
notefd = v;
write(*notefd, "interrupt", 9);
close(*notefd);
free(notefd);
}
/*
* Filename completion
*/
typedef struct Completejob Completejob;
struct Completejob
{
char *dir;
char *str;
Window *win;
};
static void
completeproc(void *arg)
{
Completejob *job;
Completion *c;
job = arg;
threadsetname("namecomplete %s", job->dir);
c = complete(job->dir, job->str);
if(c != nil && sendp(job->win->complete, c) <= 0)
freecompletion(c);
wrelease(job->win);
free(job->dir);
free(job->str);
free(job);
}
static int
windfilewidth(Window *w, uint q0, int oneelement)
{
uint q;
Rune r;
q = q0;
while(q > 0){
r = w->text.r[q-1];
if(r<=' ' || r=='=' || r=='^' || r=='(' || r=='{')
break;
if(oneelement && r=='/')
break;
--q;
}
return q0-q;
}
static void
namecomplete(Window *w)
{
Text *x;
int nstr, npath;
Rune *path, *str;
char *dir, *root;
Completejob *job;
x = &w->text;
/* control-f: filename completion; works back to white space or / */
if(x->q0<x->nr && x->r[x->q0]>' ') /* must be at end of word */
return;
nstr = windfilewidth(w, x->q0, TRUE);
str = x->r+(x->q0-nstr);
npath = windfilewidth(w, x->q0-nstr, FALSE);
path = x->r+(x->q0-nstr-npath);
/* is path rooted? if not, we need to make it relative to window path */
if(npath>0 && path[0]=='/')
dir = smprint("%.*S", npath, path);
else {
if(strcmp(w->dir, "") == 0)
root = ".";
else
root = w->dir;
dir = smprint("%s/%.*S", root, npath, path);
}
if(dir == nil)
return;
/* run in background, winctl will collect the result on w->complete chan */
job = emalloc(sizeof *job);
job->str = smprint("%.*S", nstr, str);
job->dir = cleanname(dir);
job->win = w;
incref(w);
proccreate(completeproc, job, mainstacksize);
}
static void
showcandidates(Window *w, Completion *c)
{
Text *x;
int i;
Fmt f;
Rune *rp;
uint nr, qline;
char *s;
x = &w->text;
runefmtstrinit(&f);
if (c->nmatch == 0)
s = "[no matches in ";
else
s = "[";
if(c->nfile > 32)
fmtprint(&f, "%s%d files]\n", s, c->nfile);
else{
fmtprint(&f, "%s", s);
for(i=0; i<c->nfile; i++){
if(i > 0)
fmtprint(&f, " ");
fmtprint(&f, "%s", c->filename[i]);
}
fmtprint(&f, "]\n");
}
rp = runefmtstrflush(&f);
nr = runestrlen(rp);
/* place text at beginning of line before cursor and host point */
qline = min(x->qh, x->q0);
while(qline>0 && x->r[qline-1] != '\n')
qline--;
if(qline == x->qh){
/* advance host point to avoid readback */
x->qh = xinsert(x, rp, nr, qline)+nr;
}else{
xinsert(x, rp, nr, qline);
}
free(rp);
}
void
wkeyctl(Window *w, Rune r)
{
Text *x;
int nlines, n;
int *notefd;
x = &w->text;
nlines = x->maxlines; /* need signed */
if(!w->mouseopen){
switch(r){
/* Scrolling */
case Kscrollonedown:
n = mousescrollsize(x->maxlines);
xscrolln(x, max(n, 1));
return;
case Kdown:
xscrolln(x, shiftdown ? 1 : nlines/3);
return;
case Kpgdown:
xscrolln(x, nlines*2/3);
return;
case Kscrolloneup:
n = mousescrollsize(x->maxlines);
xscrolln(x, -max(n, 1));
return;
case Kup:
xscrolln(x, -(shiftdown ? 1 : nlines/3));
return;
case Kpgup:
xscrolln(x, -nlines*2/3);
return;
case Khome:
xshow(x, 0);
return;
case Kend:
xshow(x, x->nr);
return;
/* Cursor movement */
case Kleft:
if(x->q0 > 0)
xplacetick(x, x->q0-1);
return;
case Kright:
if(x->q1 < x->nr)
xplacetick(x, x->q1+1);
return;
case CTRL('A'):
while(x->q0 > 0 && x->r[x->q0-1] != '\n' &&
x->q0 != x->qh)
x->q0--;
xplacetick(x, x->q0);
return;
case CTRL('E'):
while(x->q0 < x->nr && x->r[x->q0] != '\n')
x->q0++;
xplacetick(x, x->q0);
return;
case CTRL('B'):
xplacetick(x, x->qh);
return;
/* Hold mode */
case Kesc:
wsethold(w, !w->holdmode);
return;
case Kdel:
if(w->holdmode)
wsethold(w, 0);
break;
}
}
if(x->rawmode && (x->q0 == x->nr || w->mouseopen))
xaddraw(x, &r, 1);
else if(r == Kdel){
x->qh = x->nr;
xshow(x, x->qh);
if(w->notefd < 0)
return;
notefd = emalloc(sizeof(int));
*notefd = dup(w->notefd, -1);
proccreate(interruptproc, notefd, 4096);
}else if(r == CTRL('F') || r == Kins)
namecomplete(w);
else
xtype(x, r);
}
void
wmousectl(Window *w)
{
int but;
for(but = 1; but < 6; but++)
if(w->mc.buttons == 1<<(but-1))
goto found;
return;
found:
incref(w);
if(shiftdown && but > 3)
wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
else if(ptinrect(w->mc.xy, w->text.scrollr) || but > 3)
xscroll(&w->text, &w->mc, but);
else if(but == 1)
xselect(&w->text, &w->mc);
wrelease(w);
}
int
winctl(Window *w, int type)
{
Text *x;
int i;
x = &w->text;
switch(type){
case Closed:
wfree(w);
return 1;
case Deleted:
if(w->notefd >= 0)
write(w->notefd, "hangup", 6);
wclose(w);
break;
case Resized:
wsetname(w);
case Moved:
w->resized = TRUE;
w->wctlready = TRUE;
w->mc.buttons = 0; /* avoid re-triggering clicks on resize */
w->mq.counter++; /* cause mouse to be re-read */
wdecor(w);
break;
case Refresh:
/* TODO: clean this up? */
if(w->deleted)
break;
draw(w->img, w->img->r, x->cols[BACK], nil, ZP);
wdecor(w);
xfill(x);
x->ticked = 0;
if(x->p0 > 0)
frdrawsel(x, frptofchar(x, 0), 0, x->p0, 0);
if(x->p1 < x->nchars)
frdrawsel(x, frptofchar(x, x->p1), x->p1, x->nchars, 0);
frdrawsel(x, frptofchar(x, x->p0), x->p0, x->p1, 1);
x->lastsr = ZR;
xscrdraw(x);
break;
case Holdon:
wsethold(w, TRUE);
break;
case Holdoff:
wsethold(w, FALSE);
break;
case Rawon:
break;
case Rawoff:
// TODO: better to remove one by one? not sure if wkeyctl is safe
for(i = 0; i < x->nraw; i++)
wkeyctl(w, x->raw[i]);
x->nraw = 0;
break;
}
return 0;
}
static void
winthread(void *arg)
{
Window *w;
Text *x;
Rune r, *rp;
char *s;
int cm;
enum { AKbd, AMouse, ACtl, AConsWrite, AConsRead,
AKbdRead, AMouseRead, AWctlRead, AComplete, Agone, NALT };
Alt alts[NALT+1];
Channel *fsc;
Stringpair pair;
int i, nb, nr, initial;
uint q0;
RuneConvBuf cnv;
Mousestate m;
Completion *comp;
w = arg;
threadsetname("winthread-%d", w->id);
w->threadname = threadgetname();
x = &w->text;
nr = 0;
memset(&cnv, 0, sizeof(cnv));
fsc = chancreate(sizeof(Stringpair), 0);
alts[AKbd] = ALT(w->kbd, &s, CHANRCV);
alts[AMouse] = ALT(w->mc.c, &w->mc.Mouse, CHANRCV);
alts[ACtl] = ALT(w->ctl, &cm, CHANRCV);
alts[AConsWrite] = ALT(w->conswrite, &fsc, CHANSND);
alts[AConsRead] = ALT(w->consread, &fsc, CHANSND);
alts[AKbdRead] = ALT(w->kbdread, &fsc, CHANSND);
alts[AMouseRead] = ALT(w->mouseread, &fsc, CHANSND);
alts[AWctlRead] = ALT(w->wctlread, &fsc, CHANSND);
alts[AComplete] = ALT(w->complete, &comp, CHANRCV);
alts[Agone] = ALT(w->gone, nil, CHANNOP);
alts[NALT].op = CHANEND;
for(;;){
if(w->deleted){
alts[Agone].op = CHANSND;
alts[AConsWrite].op = CHANNOP;
alts[AConsRead].op = CHANNOP;
alts[AKbdRead].op = CHANNOP;
alts[AMouseRead].op = CHANNOP;
alts[AWctlRead].op = CHANNOP;
}else{
nr = xninput(x);
if(!w->holdmode && (nr >= 0 || cnv.n > 0 || x->rawmode && x->nraw > 0))
alts[AConsRead].op = CHANSND;
else
alts[AConsRead].op = CHANNOP;
if(w->scrolling || w->mouseopen || x->qh <= x->org+x->nchars)
alts[AConsWrite].op = CHANSND;
else
alts[AConsWrite].op = CHANNOP;
if(w->kbdopen && (w->kq.ri != w->kq.wi || w->kq.full))
alts[AKbdRead].op = CHANSND;
else
alts[AKbdRead].op = CHANNOP;
if(w->mouseopen && w->mq.counter != w->mq.lastcounter)
alts[AMouseRead].op = CHANSND;
else
alts[AMouseRead].op = CHANNOP;
alts[AWctlRead].op = w->wctlready ? CHANSND : CHANNOP;
}
switch(alt(alts)){
case AKbd:
if(!w->kq.full){
w->kq.q[w->kq.wi++] = s;
w->kq.wi %= nelem(w->kq.q);
w->kq.full = w->kq.wi == w->kq.ri;
}else
free(s);
if(!w->kbdopen)
while(w->kq.ri != w->kq.wi || w->kq.full){
s = w->kq.q[w->kq.ri++];
w->kq.ri %= nelem(w->kq.q);
w->kq.full = FALSE;
if(*s == 'c'){
chartorune(&r, s+1);
if(r)
wkeyctl(w, r);
}
free(s);
}
break;
case AKbdRead:
recv(fsc, &pair);
nb = 0;
while(w->kq.ri != w->kq.wi || w->kq.full){
s = w->kq.q[w->kq.ri];
i = strlen(s)+1;
if(nb+i > pair.ns)
break;
w->kq.ri = (w->kq.ri+1) % nelem(w->kq.q);
w->kq.full = FALSE;
memmove((char*)pair.s + nb, s, i);
free(s);
nb += i;
}
pair.ns = nb;
send(fsc, &pair);
break;
case AMouse:
if(w->mouseopen){
Mousestate *mp;
w->mq.counter++;
/* queue click events in ring buffer.
* pure movement only in else branch of the case below */
if(!w->mq.full && w->mq.lastb != w->mc.buttons){
mp = &w->mq.q[w->mq.wi++];
w->mq.wi %= nelem(w->mq.q);
w->mq.full = w->mq.wi == w->mq.ri;
mp->Mouse = w->mc;
mp->counter = w->mq.counter;
w->mq.lastb = w->mc.buttons;
}
}else
wmousectl(w);
break;
case AMouseRead:
recv(fsc, &pair);
w->mq.full = FALSE;
/* first return queued clicks, then current state */
if(w->mq.wi != w->mq.ri){
m = w->mq.q[w->mq.ri++];
w->mq.ri %= nelem(w->mq.q);
}else
m = (Mousestate){w->mc.Mouse, w->mq.counter};
w->mq.lastcounter = m.counter;
pair.ns = snprint(pair.s, pair.ns, "%c%11d %11d %11d %11ld ",
"mr"[w->resized], m.xy.x, m.xy.y, m.buttons, m.msec);
w->resized = FALSE;
send(fsc, &pair);
break;
case AConsWrite:
recv(fsc, &pair);
initial = handlebs(&pair);
if(initial){
initial = min(initial, x->qh);
xdelete(x, x->qh-initial, x->qh);
}
x->qh = xinsert(x, pair.s, pair.ns, x->qh) + pair.ns;
free(pair.s);
if(w->scrolling || w->mouseopen)
xshow(x, x->qh);
xscrdraw(x);
break;
case AConsRead:
recv(fsc, &pair);
cnvsize(&cnv, pair.ns);
nr = r2bfill(&cnv, x->r+x->qh, nr);
x->qh += nr;
/* if flushed by ^D, skip the ^D */
if(!(nr > 0 && x->r[x->qh-1] == '\n') &&
x->qh < x->nr && x->r[x->qh] == CTRL('D'))
x->qh++;
if(x->rawmode){
nr = r2bfill(&cnv, x->raw, x->nraw);
x->nraw -= nr;
runemove(x->raw, x->raw+nr, x->nraw);
}
r2bfinish(&cnv, &pair);
send(fsc, &pair);
break;
case AWctlRead:
w->wctlready = FALSE;
recv(fsc, &pair);
pair.ns = snprint(pair.s, pair.ns, "%11d %11d %11d %11d %11s %11s ",
w->img->r.min.x, w->img->r.min.y, w->img->r.max.x, w->img->r.max.y,
w == focused ? "current" : "notcurrent",
w->hidden ? "hidden" : "visible");
send(fsc, &pair);
break;
case ACtl:
if(winctl(w, cm)){
free(cnv.buf);
return;
}
break;
case AComplete:
if(w->img!=nil){
if(!comp->advance)
showcandidates(w, comp);
if(comp->advance){
rp = runesmprint("%s", comp->string);
if(rp){
nr = runestrlen(rp);
q0 = x->q0;
q0 = xinsert(x, rp, nr, q0);
xshow(x, q0+nr);
free(rp);
}
}
}
freecompletion(comp);
break;
}
flushimage(display, 1);
}
}
static void
shellproc(void *args)
{
Window *w;
Channel *pidc;
void **arg;
char *cmd, *dir;
char **argv;
arg = args;
w = arg[0];
pidc = arg[1];
cmd = arg[2];
argv = arg[3];
dir = arg[4];
rfork(RFNAMEG|RFFDG|RFENVG);
if(fsmount(w->id) < 0){
fprint(2, "mount failed: %r\n");
sendul(pidc, 0);
threadexits("mount failed");
}
close(0);
if(open("/dev/cons", OREAD) < 0){
fprint(2, "can't open /dev/cons: %r\n");
sendul(pidc, 0);
threadexits("/dev/cons");
}
close(1);
if(open("/dev/cons", OWRITE) < 0){
fprint(2, "can't open /dev/cons: %r\n");
sendul(pidc, 0);
threadexits("open"); /* BUG? was terminate() */
}
if(wrelease(w) == 0){ /* remove extra ref hanging from creation */
notify(nil);
dup(1, 2);
if(dir)
chdir(dir);
procexec(pidc, cmd, argv);
_exits("exec failed");
}
}
int
wincmd(Window *w, int pid, char *dir, char **argv)
{
Channel *cpid;
void *args[5];
if(argv){
cpid = chancreate(sizeof(int), 0);
assert(cpid);
args[0] = w;
args[1] = cpid;
args[2] = "/bin/rc";
args[3] = argv;
args[4] = dir;
proccreate(shellproc, args, mainstacksize);
pid = recvul(cpid);
chanfree(cpid);
if(pid == 0){
wdelete(w);
return 0;
}
}
wsetpid(w, pid, 1);
if(dir){
free(w->dir);
w->dir = estrdup(dir);
}
return pid;
}