#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"
/*
* WASHINGTON (AP) - The Food and Drug Administration warned
* consumers Wednesday not to use ``Rio'' hair relaxer products
* because they may cause severe hair loss or turn hair green....
* The FDA urged consumers who have experienced problems with Rio
* to notify their local FDA office, local health department or the
* company at 1‑800‑543‑3002.
*/
void resize(void);
void move(void);
void delete(void);
void hide(void);
void unhide(int);
void newtile(int);
Image *sweep(void);
Image *bandsize(Window*);
Image* drag(Window*, Rectangle*);
void refresh(Rectangle);
void resized(void);
Channel *exitchan; /* chan(int) */
Channel *winclosechan; /* chan(Window*); */
Rectangle viewr;
int threadrforkflag = 0; /* should be RFENVG but that hides rio from plumber */
void mousethread(void*);
void keyboardthread(void*);
void winclosethread(void*);
void deletethread(void*);
void initcmd(void*);
char *fontname, *backname;
int mainpid;
enum
{
New,
Reshape,
Move,
Delete,
Hide,
Exit,
};
enum
{
Cut,
Paste,
Snarf,
Plumb,
Send,
Scroll,
};
char *menu2str[] = {
[Cut] "cut",
[Paste] "paste",
[Snarf] "snarf",
[Plumb] "plumb",
[Send] "send",
[Scroll] "scroll",
nil
};
Menu menu2 =
{
menu2str
};
int Hidden = Exit+1;
char *menu3str[8 + nelem(hidden)] = {
[New] "New",
[Reshape] "Resize",
[Move] "Move",
[Delete] "Delete",
[Hide] "Hide",
[Exit] "Exit",
nil
};
Menu menu3 =
{
menu3str
};
char *rcargv[] = { "rc", "-i", nil };
char *kbdargv[] = { "rc", "-c", nil, nil };
int errorshouldabort = 0;
void
derror(Display*, char *errorstr)
{
error(errorstr);
}
void
usage(void)
{
fprint(2, "usage: rio [-b background] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
exits("usage");
}
void
threadmain(int argc, char *argv[])
{
char *initstr, *kbdin, *s;
static void *arg[1];
char buf[256];
Image *i;
Rectangle r;
if(strstr(argv[0], ".out") == nil){
menu3str[Exit] = nil;
Hidden--;
}
initstr = nil;
kbdin = nil;
maxtab = 0;
ARGBEGIN{
case 'b':
backname = ARGF();
if(backname == nil)
usage();
break;
case 'f':
fontname = ARGF();
if(fontname == nil)
usage();
break;
case 'i':
initstr = ARGF();
if(initstr == nil)
usage();
break;
case 'k':
if(kbdin != nil)
usage();
kbdin = ARGF();
if(kbdin == nil)
usage();
break;
case 's':
scrolling = TRUE;
break;
}ARGEND
mainpid = getpid();
if(getwd(buf, sizeof buf) == nil)
startdir = estrdup(".");
else
startdir = estrdup(buf);
if(fontname == nil)
fontname = getenv("font");
if(fontname == nil)
fontname = "/lib/font/bit/lucm/unicode.9.font";
s = getenv("tabstop");
if(s != nil)
maxtab = strtol(s, nil, 0);
if(maxtab == 0)
maxtab = 4;
free(s);
/* check font before barging ahead */
if(access(fontname, 0) < 0){
fprint(2, "rio: can't access %s: %r\n", fontname);
exits("font open");
}
putenv("font", fontname);
snarffd = open("/dev/snarf", OREAD|OCEXEC);
if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
fprint(2, "rio: can't open display: %r\n");
exits("display open");
}
iconinit(backname);
view = screen;
viewr = view->r;
mousectl = initmouse(nil, screen);
if(mousectl == nil)
error("can't find mouse");
mouse = mousectl;
keyboardctl = initkeyboard(nil);
if(keyboardctl == nil)
error("can't find keyboard");
wscreen = allocscreen(screen, background, 0);
if(wscreen == nil)
error("can't allocate screen");
draw(view, viewr, background, nil, viewr.min);
flushimage(display, 1);
exitchan = chancreate(sizeof(int), 0);
winclosechan = chancreate(sizeof(Window*), 0);
deletechan = chancreate(sizeof(char*), 0);
timerinit();
threadcreate(keyboardthread, nil, STACK);
threadcreate(mousethread, nil, STACK);
threadcreate(winclosethread, nil, STACK);
threadcreate(deletethread, nil, STACK);
filsys = filsysinit(xfidinit());
if(filsys == nil)
fprint(2, "rio: can't create file system server: %r\n");
else{
errorshouldabort = 1; /* suicide if there's trouble after this */
if(initstr)
proccreate(initcmd, initstr, STACK);
if(kbdin){
kbdargv[2] = kbdin;
r = screen->r;
r.max.x = r.min.x+300;
r.max.y = r.min.y+80;
i = allocwindow(wscreen, r, Refbackup, DWhite);
wkeyboard = new(i, FALSE, scrolling, 0, nil, "/bin/rc", kbdargv);
if(wkeyboard == nil)
error("can't create keyboard window");
}
threadnotify(shutdown, 1);
recv(exitchan, nil);
}
killprocs();
threadexitsall(nil);
}
/*
* /dev/snarf updates when the file is closed, so we must open our own
* fd here rather than use snarffd
*/
void
putsnarf(void)
{
int fd, i, n;
if(snarffd<0 || nsnarf==0)
return;
fd = open("/dev/snarf", OWRITE);
if(fd < 0)
return;
/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
for(i=0; i<nsnarf; i+=n){
n = nsnarf-i;
if(n >= 256)
n = 256;
if(fprint(fd, "%.*S", n, snarf+i) < 0)
break;
}
close(fd);
}
void
getsnarf(void)
{
int i, n, nb, nulls;
char *sn, buf[1024];
if(snarffd < 0)
return;
sn = nil;
i = 0;
seek(snarffd, 0, 0);
while((n = read(snarffd, buf, sizeof buf)) > 0){
sn = erealloc(sn, i+n+1);
memmove(sn+i, buf, n);
i += n;
sn[i] = 0;
}
if(i > 0){
snarf = runerealloc(snarf, i+1);
cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
free(sn);
}
}
void
initcmd(void *arg)
{
char *cmd;
cmd = arg;
rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
procexecl(nil, "/bin/rc", "rc", "-c", cmd, nil);
fprint(2, "rio: exec failed: %r\n");
exits("exec");
}
char *oknotes[] =
{
"delete",
"hangup",
"kill",
"exit",
nil
};
int
shutdown(void *, char *msg)
{
int i;
killprocs();
for(i=0; oknotes[i]; i++)
if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
threadexitsall(msg);
fprint(2, "rio %d: abort: %s\n", getpid(), msg);
abort();
exits(msg);
return 0;
}
void
killprocs(void)
{
int i;
for(i=0; i<nwindow; i++)
postnote(PNGROUP, window[i]->pid, "hangup");
}
void
nextwin(void)
{
Window *w;
int i, j, k, t;
k = 0;
t = 0;
for(i=0; i<nwindow; i++){
if(window[i]->topped > t){
k = i;
t = window[i]->topped;
}
}
i = k;
k = nwindow;
while(k-- > 0){
i = (i + 1) % nwindow;
if(w = window[i]){
for(j=0; j<nhidden; j++){
if(hidden[j] == w)
goto skip;
}
topwindow(w->i);
wtopme(w);
break;
skip: continue;
}
}
}
void
keyboardthread(void*)
{
Rune buf[2][20], *rp;
int n, i;
static int switcher;
threadsetname("keyboardthread");
n = 0;
for(;;){
rp = buf[n];
n = 1-n;
recv(keyboardctl->c, rp);
for(i=1; i<nelem(buf[0])-1; i++)
if(nbrecv(keyboardctl->c, rp+i) <= 0)
break;
rp[i] = L'\0';
if(input && strcmp(input->label, "rio")==0){
if(((nwindow - nhidden) > 1) && *rp == 0xe){
nextwin();
continue;
}
} else {
if(*rp == 0xd || *rp == 0xe){
nextwin();
continue;
}
}
if(input != nil)
sendp(input->ck, rp);
}
}
/*
* Used by /dev/kbdin
*/
void
keyboardsend(char *s, int cnt)
{
Rune *r;
int i, nb, nr;
r = runemalloc(cnt);
/* BUGlet: partial runes will be converted to error runes */
cvttorunes(s, cnt, r, &nb, &nr, nil);
for(i=0; i<nr; i++)
send(keyboardctl->c, &r[i]);
free(r);
}
int
portion(int x, int lo, int hi)
{
x -= lo;
hi -= lo;
if(x < 20)
return 0;
if(x > hi-20)
return 2;
return 1;
}
int
whichcorner(Window *w, Point p)
{
int i, j;
i = portion(p.x, w->screenr.min.x, w->screenr.max.x);
j = portion(p.y, w->screenr.min.y, w->screenr.max.y);
return 3*j+i;
}
void
cornercursor(Window *w, Point p, int force)
{
if(w!=nil && winborder(w, p))
riosetcursor(corners[whichcorner(w, p)], force);
else
wsetcursor(w, force);
}
/* thread to allow fsysproc to synchronize window closing with main proc */
void
winclosethread(void*)
{
Window *w;
threadsetname("winclosethread");
for(;;){
w = recvp(winclosechan);
wclose(w);
}
}
/* thread to make Deleted windows that the client still holds disappear offscreen after an interval */
void
deletethread(void*)
{
char *s;
Image *i;
threadsetname("deletethread");
for(;;){
s = recvp(deletechan);
i = namedimage(display, s);
if(i != nil){
/* move it off-screen to hide it, since client is slow in letting it go */
originwindow(i, i->r.min, view->r.max);
}
freeimage(i);
free(s);
}
}
void
deletetimeoutproc(void *v)
{
char *s;
s = v;
sleep(750); /* remove window from screen after 3/4 of a second */
sendp(deletechan, s);
}
/*
* Button 6 - keyboard toggle - has been pressed.
* Send event to keyboard, wait for button up, send that.
* Note: there is no coordinate translation done here; this
* is just about getting button 6 to the keyboard simulator.
*/
void
keyboardhide(void)
{
send(wkeyboard->mc.c, mouse);
do
readmouse(mousectl);
while(mouse->buttons & (1<<5));
send(wkeyboard->mc.c, mouse);
}
void
mousethread(void*)
{
int sending, inside, scrolling, moving, band;
Window *oin, *w, *winput;
Image *i;
Rectangle r;
Point xy;
Mouse tmp;
enum {
MReshape,
MMouse,
NALT
};
static Alt alts[NALT+1];
threadsetname("mousethread");
sending = FALSE;
scrolling = FALSE;
moving = FALSE;
alts[MReshape].c = mousectl->resizec;
alts[MReshape].v = nil;
alts[MReshape].op = CHANRCV;
alts[MMouse].c = mousectl->c;
alts[MMouse].v = &mousectl->Mouse;
alts[MMouse].op = CHANRCV;
alts[NALT].op = CHANEND;
for(;;)
switch(alt(alts)){
case MReshape:
resized();
break;
case MMouse:
if(wkeyboard!=nil && (mouse->buttons & (1<<5))){
keyboardhide();
break;
}
Again:
winput = input;
/* override everything for the keyboard window */
if(wkeyboard!=nil && ptinrect(mouse->xy, wkeyboard->screenr)){
/* make sure it's on top; this call is free if it is */
wtopme(wkeyboard);
winput = wkeyboard;
}
if(winput!=nil && winput->i!=nil){
/* convert to logical coordinates */
xy.x = mouse->xy.x + (winput->i->r.min.x-winput->screenr.min.x);
xy.y = mouse->xy.y + (winput->i->r.min.y-winput->screenr.min.y);
/* the up and down scroll buttons are not subject to the usual rules */
if((mouse->buttons&(8|16)) && !winput->mouseopen)
goto Sending;
inside = ptinrect(mouse->xy, insetrect(winput->screenr, Selborder));
if(winput->mouseopen)
scrolling = FALSE;
else if(scrolling)
scrolling = mouse->buttons;
else
scrolling = mouse->buttons && ptinrect(xy, winput->scrollr);
/* topped will be zero if window has been bottomed */
if(sending == FALSE && !scrolling && winborder(winput, mouse->xy) && winput->topped>0){
moving = TRUE;
}else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
sending = TRUE;
}else
sending = FALSE;
w = wpointto(mouse->xy);
if((w != nil) && (w != input) && (!moving) && (!mouse->buttons)){
wcurrent(w);
wborder(w, Selborder);
flushimage(display, 1);
continue;
}
if(sending){
Sending:
if(mouse->buttons == 0){
cornercursor(winput, mouse->xy, 0);
sending = FALSE;
}else
wsetcursor(winput, 0);
tmp = mousectl->Mouse;
tmp.xy = xy;
send(winput->mc.c, &tmp);
continue;
}
/* change cursor if over anyone's border */
if(w != nil)
cornercursor(w, mouse->xy, 0);
else
riosetcursor(nil, 0);
if(moving && (mouse->buttons&7)){
oin = winput;
band = mouse->buttons & 3;
sweeping = 1;
if(band)
i = bandsize(winput);
else
i = drag(winput, &r);
sweeping = 0;
if(i != nil){
if(winput == oin){
if(band)
wsendctlmesg(winput, Reshaped, i->r, i);
else
wsendctlmesg(winput, Moved, r, i);
cornercursor(winput, mouse->xy, 1);
}else
freeimage(i);
}
}
if(w != nil)
cornercursor(w, mouse->xy, 0);
/* we're not sending the event, but if button is down maybe we should */
if(mouse->buttons){
/* w->topped will be zero if window has been bottomed */
if(w==nil || (w==winput && w->topped>0)){
if(mouse->buttons & 1){
;
}else if(mouse->buttons & 2){
if(winput && !winput->mouseopen)
button2menu(winput);
}else if(mouse->buttons & 4)
button3menu();
}else{
/* if button 1 event in the window, top the window and wait for button up. */
/* otherwise, top the window and pass the event on */
if(wtop(mouse->xy) && (mouse->buttons!=1 || winborder(w, mouse->xy)))
goto Again;
goto Drain;
}
}
moving = FALSE;
break;
Drain:
do
readmouse(mousectl);
while(mousectl->buttons);
moving = FALSE;
goto Again; /* recalculate mouse position, cursor */
}
}
void
resized(void)
{
Image *im;
int i, j, ishidden;
Rectangle r;
Point o, n;
Window *w;
if(getwindow(display, Refnone) < 0)
error("failed to re-attach window");
freescrtemps();
view = screen;
freescreen(wscreen);
wscreen = allocscreen(screen, background, 0);
if(wscreen == nil)
error("can't re-allocate screen");
draw(view, view->r, background, nil, view->r.min);
o = subpt(viewr.max, viewr.min);
n = subpt(view->clipr.max, view->clipr.min);
for(i=0; i<nwindow; i++){
w = window[i];
if(w->deleted)
continue;
r = rectsubpt(w->i->r, viewr.min);
/*
r.min.x = (r.min.x*n.x)/o.x;
r.min.y = (r.min.y*n.y)/o.y;
r.max.x = (r.max.x*n.x)/o.x;
r.max.y = (r.max.y*n.y)/o.y;
if(Dx(r)<100){
r.max.x = r.min.x + 100;
}
if(Dy(r)<3*font->height){
r.max.y = r.min.y + 3*font->height;
}
*/
r = rectaddpt(r, screen->clipr.min);
ishidden = 0;
for(j=0; j<nhidden; j++)
if(w == hidden[j]){
ishidden = 1;
break;
}
if(ishidden){
im = allocimage(display, r, screen->chan, 0, DWhite);
r = ZR;
}else
im = allocwindow(wscreen, r, Refbackup, DWhite);
if(im)
wsendctlmesg(w, Reshaped, r, im);
}
viewr = screen->r;
flushimage(display, 1);
}
void
button3menu(void)
{
int i;
menu3str[Hidden] = "[Hidden]";
for(i=0; i<nhidden; i++)
menu3str[i+Hidden+1] = hidden[i]->label;
menu3str[i+Hidden+1] = nil;
sweeping = 1;
switch(i = piemenuhit(3, mousectl, &menu3, wscreen)){
case -1:
break;
case New:
new(sweep(), FALSE, scrolling, 0, nil, "/bin/rc", nil);
break;
case Reshape:
resize();
break;
case Move:
move();
break;
case Delete:
delete();
break;
case Hide:
hide();
break;
case Exit:
if(Hidden > Exit){
send(exitchan, nil);
break;
}
/* else fall through */
default:
unhide(i);
break;
}
sweeping = 0;
}
void
button2menu(Window *w)
{
if(w->deleted)
return;
incref(w);
if(w->scrolling)
menu2str[Scroll] = "noscroll";
else
menu2str[Scroll] = "scroll";
switch(piemenuhit(2, mousectl, &menu2, wscreen)){
case Cut:
wsnarf(w);
wcut(w);
wscrdraw(w);
break;
case Snarf:
wsnarf(w);
break;
case Paste:
getsnarf();
wpaste(w);
wscrdraw(w);
break;
case Plumb:
wplumb(w);
break;
case Send:
getsnarf();
wsnarf(w);
if(nsnarf == 0)
break;
if(w->rawing){
waddraw(w, snarf, nsnarf);
if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
waddraw(w, L"\n", 1);
}else{
winsert(w, snarf, nsnarf, w->nr);
if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
winsert(w, L"\n", 1, w->nr);
}
wsetselect(w, w->nr, w->nr);
wshow(w, w->nr);
break;
case Scroll:
if(w->scrolling ^= 1)
wshow(w, w->nr);
break;
}
wclose(w);
wsendctlmesg(w, Wakeup, ZR, nil);
flushimage(display, 1);
}
Point
onscreen(Point p)
{
p.x = max(screen->clipr.min.x, p.x);
p.x = min(screen->clipr.max.x, p.x);
p.y = max(screen->clipr.min.y, p.y);
p.y = min(screen->clipr.max.y, p.y);
return p;
}
Image*
sweep(void)
{
Image *i, *oi;
Rectangle r;
Point p0, p;
i = nil;
menuing = TRUE;
riosetcursor(&crosscursor, 1);
while(mouse->buttons == 0)
readmouse(mousectl);
p0 = onscreen(mouse->xy);
p = p0;
r.min = p;
r.max = p;
oi = nil;
while(mouse->buttons == 4){
readmouse(mousectl);
if(mouse->buttons != 4 && mouse->buttons != 0)
break;
if(!eqpt(mouse->xy, p)){
p = onscreen(mouse->xy);
r = canonrect(Rpt(p0, p));
if(Dx(r)>5 && Dy(r)>5){
i = allocwindow(wscreen, r, Refnone, 0xEEEEEEFF); /* grey */
freeimage(oi);
if(i == nil)
goto Rescue;
oi = i;
border(i, r, Selborder, red, ZP);
flushimage(display, 1);
}
}
}
if(mouse->buttons != 0)
goto Rescue;
if(i==nil || Dx(i->r)<100 || Dy(i->r)<3*font->height)
goto Rescue;
oi = i;
i = allocwindow(wscreen, oi->r, Refbackup, DWhite);
freeimage(oi);
if(i == nil)
goto Rescue;
border(i, r, Selborder, red, ZP);
cornercursor(input, mouse->xy, 1);
goto Return;
Rescue:
freeimage(i);
i = nil;
cornercursor(input, mouse->xy, 1);
while(mouse->buttons)
readmouse(mousectl);
Return:
moveto(mousectl, mouse->xy); /* force cursor update; ugly */
menuing = FALSE;
return i;
}
void
drawedge(Image **bp, Rectangle r)
{
Image *b = *bp;
if(b != nil && Dx(b->r) == Dx(r) && Dy(b->r) == Dy(r))
originwindow(b, r.min, r.min);
else{
freeimage(b);
*bp = allocwindow(wscreen, r, Refbackup, DRed);
}
}
void
drawborder(Rectangle r, int show)
{
static Image *b[4];
int i;
if(show == 0){
for(i = 0; i < 4; i++){
freeimage(b[i]);
b[i] = nil;
}
}else{
r = canonrect(r);
drawedge(&b[0], Rect(r.min.x, r.min.y, r.min.x+Borderwidth, r.max.y));
drawedge(&b[1], Rect(r.min.x+Borderwidth, r.min.y, r.max.x-Borderwidth, r.min.y+Borderwidth));
drawedge(&b[2], Rect(r.max.x-Borderwidth, r.min.y, r.max.x, r.max.y));
drawedge(&b[3], Rect(r.min.x+Borderwidth, r.max.y-Borderwidth, r.max.x-Borderwidth, r.max.y));
}
}
Image*
drag(Window *w, Rectangle *rp)
{
Image *i, *ni;
Point p, op, d, dm, om;
Rectangle r;
i = w->i;
menuing = TRUE;
om = mouse->xy;
riosetcursor(&boxcursor, 1);
dm = subpt(mouse->xy, w->screenr.min);
d = subpt(i->r.max, i->r.min);
op = subpt(mouse->xy, dm);
drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 1);
flushimage(display, 1);
while(mouse->buttons == 4){
p = subpt(mouse->xy, dm);
if(!eqpt(p, op)){
drawborder(Rect(p.x, p.y, p.x+d.x, p.y+d.y), 1);
flushimage(display, 1);
op = p;
}
readmouse(mousectl);
}
r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
drawborder(r, 0);
cornercursor(w, mouse->xy, 1);
moveto(mousectl, mouse->xy); /* force cursor update; ugly */
menuing = FALSE;
flushimage(display, 1);
if(mouse->buttons!=0 || (ni=allocwindow(wscreen, r, Refbackup, DWhite))==nil){
moveto(mousectl, om);
while(mouse->buttons)
readmouse(mousectl);
*rp = Rect(0, 0, 0, 0);
return nil;
}
draw(ni, ni->r, i, nil, i->r.min);
*rp = r;
return ni;
}
Point
cornerpt(Rectangle r, Point p, int which)
{
switch(which){
case 0: /* top left */
p = Pt(r.min.x, r.min.y);
break;
case 2: /* top right */
p = Pt(r.max.x,r.min.y);
break;
case 6: /* bottom left */
p = Pt(r.min.x, r.max.y);
break;
case 8: /* bottom right */
p = Pt(r.max.x, r.max.y);
break;
case 1: /* top edge */
p = Pt(p.x,r.min.y);
break;
case 5: /* right edge */
p = Pt(r.max.x, p.y);
break;
case 7: /* bottom edge */
p = Pt(p.x, r.max.y);
break;
case 3: /* left edge */
p = Pt(r.min.x, p.y);
break;
}
return p;
}
Rectangle
whichrect(Rectangle r, Point p, int which)
{
switch(which){
case 0: /* top left */
r = Rect(p.x, p.y, r.max.x, r.max.y);
break;
case 2: /* top right */
r = Rect(r.min.x, p.y, p.x, r.max.y);
break;
case 6: /* bottom left */
r = Rect(p.x, r.min.y, r.max.x, p.y);
break;
case 8: /* bottom right */
r = Rect(r.min.x, r.min.y, p.x, p.y);
break;
case 1: /* top edge */
r = Rect(r.min.x, p.y, r.max.x, r.max.y);
break;
case 5: /* right edge */
r = Rect(r.min.x, r.min.y, p.x, r.max.y);
break;
case 7: /* bottom edge */
r = Rect(r.min.x, r.min.y, r.max.x, p.y);
break;
case 3: /* left edge */
r = Rect(p.x, r.min.y, r.max.x, r.max.y);
break;
}
return canonrect(r);
}
Image*
bandsize(Window *w)
{
Image *i;
Rectangle r, or;
Point p, startp;
int which, but;
p = mouse->xy;
which = whichcorner(w, p);
p = cornerpt(w->screenr, p, which);
wmovemouse(w, p);
readmouse(mousectl);
r = whichrect(w->screenr, p, which);
drawborder(r, 1);
or = r;
startp = p;
but = mouse->buttons;
while(mouse->buttons == but){
p = onscreen(mouse->xy);
r = whichrect(w->screenr, p, which);
if(!eqrect(r, or) && goodrect(r)){
drawborder(r, 1);
flushimage(display, 1);
or = r;
}
readmouse(mousectl);
}
p = mouse->xy;
drawborder(or, 0);
flushimage(display, 1);
wsetcursor(w, 1);
if(mouse->buttons!=0 || Dx(or)<100 || Dy(or)<3*font->height){
while(mouse->buttons)
readmouse(mousectl);
return nil;
}
if(abs(p.x-startp.x)+abs(p.y-startp.y) <= 1)
return nil;
i = allocwindow(wscreen, or, Refbackup, DWhite);
if(i == nil)
return nil;
border(i, r, Selborder, red, ZP);
return i;
}
Window*
pointto(int wait)
{
Window *w;
menuing = TRUE;
riosetcursor(&sightcursor, 1);
while(mouse->buttons == 0)
readmouse(mousectl);
if(mouse->buttons == 4)
w = wpointto(mouse->xy);
else
w = nil;
if(wait)
while(mouse->buttons){
if(mouse->buttons!=4 && w !=nil){ /* cancel */
cornercursor(input, mouse->xy, 0);
w = nil;
}
readmouse(mousectl);
}
cornercursor(input, mouse->xy, 0);
moveto(mousectl, mouse->xy); /* force cursor update; ugly */
menuing = FALSE;
return w;
}
void
delete(void)
{
Window *w;
w = pointto(TRUE);
if(w)
wsendctlmesg(w, Deleted, ZR, nil);
}
void
resize(void)
{
Window *w;
Image *i;
w = pointto(TRUE);
if(w == nil)
return;
i = sweep();
if(i)
wsendctlmesg(w, Reshaped, i->r, i);
}
void
move(void)
{
Window *w;
Image *i;
Rectangle r;
w = pointto(FALSE);
if(w == nil)
return;
i = drag(w, &r);
if(i)
wsendctlmesg(w, Moved, r, i);
cornercursor(input, mouse->xy, 1);
}
int
whide(Window *w)
{
Image *i;
int j;
for(j=0; j<nhidden; j++)
if(hidden[j] == w) /* already hidden */
return -1;
if(nhidden == nelem(hidden))
return 0;
i = allocimage(display, w->screenr, w->i->chan, 0, DWhite);
if(i){
hidden[nhidden++] = w;
wsendctlmesg(w, Reshaped, ZR, i);
return 1;
}
return 0;
}
int
wunhide(int h)
{
Image *i;
Window *w;
w = hidden[h];
i = allocwindow(wscreen, w->i->r, Refbackup, DWhite);
if(i){
--nhidden;
memmove(hidden+h, hidden+h+1, (nhidden-h)*sizeof(Window*));
wsendctlmesg(w, Reshaped, w->i->r, i);
return 1;
}
return 0;
}
void
hide(void)
{
Window *w;
w = pointto(TRUE);
if(w == nil)
return;
whide(w);
}
void
unhide(int h)
{
Window *w;
h -= Hidden+1;
if(h < 0)
return;
w = hidden[h];
if(w == nil)
return;
wunhide(h);
}
Window*
new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **argv)
{
Window *w;
Mousectl *mc;
Channel *cm, *ck, *cctl, *cpid;
void **arg;
if(i == nil)
return nil;
cm = chancreate(sizeof(Mouse), 0);
ck = chancreate(sizeof(Rune*), 0);
cctl = chancreate(sizeof(Wctlmesg), 4);
cpid = chancreate(sizeof(int), 0);
if(cm==nil || ck==nil || cctl==nil)
error("new: channel alloc failed");
mc = emalloc(sizeof(Mousectl));
*mc = *mousectl;
mc->image = i;
mc->c = cm;
w = wmk(i, mc, ck, cctl, scrollit);
free(mc); /* wmk copies *mc */
window = erealloc(window, ++nwindow*sizeof(Window*));
window[nwindow-1] = w;
if(hideit){
hidden[nhidden++] = w;
w->screenr = ZR;
}
threadcreate(winctl, w, 8192);
if(!hideit)
wcurrent(w);
flushimage(display, 1);
if(pid == 0){
arg = emalloc(5*sizeof(void*));
arg[0] = w;
arg[1] = cpid;
arg[2] = cmd;
if(argv == nil)
arg[3] = rcargv;
else
arg[3] = argv;
arg[4] = dir;
proccreate(winshell, arg, 8192);
pid = recvul(cpid);
free(arg);
}
if(pid == 0){
/* window creation failed */
wsendctlmesg(w, Deleted, ZR, nil);
chanfree(cpid);
return nil;
}
wsetpid(w, pid, 1);
wsetname(w);
if(dir)
w->dir = estrdup(dir);
chanfree(cpid);
return w;
}
|