jehanne/sys/src/cmd/rio/rio.c

1326 lines
26 KiB
C
Raw Permalink Normal View History

2018-01-06 01:08:25 +01:00
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
* See /doc/license/gpl-2.0.txt for details about the licensing.
*/
/* Portions of this file are Copyright (C) 9front's team.
* See /doc/license/9front-mit for details about the licensing.
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
2018-01-06 01:08:25 +01:00
*/
#include <u.h>
#include <lib9.h>
#include <envvars.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include <9P2000.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 18005433002.
*/
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*);
void resized(void);
Channel *exitchan; /* chan(int) */
Channel *winclosechan; /* chan(Window*); */
Channel *kbdchan; /* chan(char*); */
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*);
Channel* initkbd(void);
char *fontname;
enum
{
New,
Reshape,
Move,
Delete,
Hide,
Exit,
};
enum
{
Cut,
Paste,
Snarf,
Plumb,
Look,
Send,
Scroll,
};
char *menu2str[] = {
[Cut] "cut",
[Paste] "paste",
[Snarf] "snarf",
[Plumb] "plumb",
[Look] "look",
[Send] "send",
[Scroll] "scroll",
nil
};
Menu menu2 =
{
menu2str
};
int Hidden = Exit+1;
char *menu3str[100] = {
[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] [-f font] [-i initcmd] [-k kbdcmd] [-s]\n");
exits("usage");
}
void
threadmain(int argc, char *argv[])
{
char *initstr, *kbdin, *s;
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':
reverse = ~0xFF;
break;
case 'f':
fontname = EARGF(usage());
break;
case 'i':
initstr = EARGF(usage());
break;
case 'k':
if(kbdin != nil)
usage();
kbdin = EARGF(usage());
break;
case 's':
scrolling = TRUE;
break;
case 'D':
debug++;
break;
default:
usage();
}ARGEND
if(getwd(buf, sizeof buf) <= 0)
startdir = estrdup(".");
else
startdir = estrdup(buf);
if(fontname == nil)
fontname = getenv("font");
if(fontname == nil)
fontname = "/lib/font/bit/fixed/unicode.6x13.font";
s = getenv("tabstop");
if(s != nil)
maxtab = strtol(s, nil, 0);
if(maxtab == 0)
maxtab = 4;
free(s);
s = getenv(ENV_CPUTYPE);
if(s){
snprint(buf, sizeof(buf), "/arch/%s/aux/rio", s);
2019-11-26 02:25:23 +01:00
sys_bind(buf, "/cmd", MBEFORE);
free(s);
}
2019-11-26 02:25:23 +01:00
sys_bind("/arch/rc/aux/rio", "/cmd", MBEFORE);
/* 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);
2019-11-26 02:25:23 +01:00
snarffd = sys_open("/dev/snarf", OREAD|OCEXEC);
gotscreen = access("/dev/screen", AEXIST)==0;
if(geninitdraw(nil, derror, nil, "rio", nil, Refnone) < 0){
fprint(2, "rio: can't open display: %r\n");
exits("display open");
}
iconinit();
exitchan = chancreate(sizeof(int), 0);
winclosechan = chancreate(sizeof(Window*), 0);
deletechan = chancreate(sizeof(char*), 0);
view = screen;
viewr = view->r;
mousectl = initmouse(nil, screen);
if(mousectl == nil)
error("can't find mouse");
mouse = mousectl;
kbdchan = initkbd();
if(kbdchan == nil)
error("can't find keyboard");
wscreen = allocscreen(screen, background, 0);
if(wscreen == nil)
error("can't allocate screen");
draw(view, viewr, background, nil, ZP);
flushimage(display, 1);
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.min.y = r.max.y-Dy(r)/3;
i = allocwindow(wscreen, r, Refbackup, DNofill);
wkeyboard = new(i, FALSE, scrolling, 0, nil, "/cmd/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;
2019-11-26 02:25:23 +01:00
fd = sys_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;
}
2019-11-26 02:25:23 +01:00
sys_close(fd);
}
void
getsnarf(void)
{
int i, n, nb, nulls;
char *s, *sn;
if(snarffd < 0)
return;
sn = nil;
i = 0;
2019-11-26 02:25:23 +01:00
sys_seek(snarffd, 0, 0);
for(;;){
if(i > MAXSNARF)
break;
if((s = realloc(sn, i+1024+1)) == nil)
break;
sn = s;
2019-11-26 02:25:23 +01:00
if((n = jehanne_read(snarffd, sn+i, 1024)) <= 0)
break;
i += n;
}
if(i == 0)
return;
sn[i] = 0;
if((snarf = runerealloc(snarf, i+1)) != nil)
cvttorunes(sn, i, snarf, &nb, &nsnarf, &nulls);
free(sn);
}
void
initcmd(void *arg)
{
char *cmd;
cmd = arg;
2019-11-26 02:25:23 +01:00
sys_rfork(RFENVG|RFFDG|RFNOTEG|RFNAMEG);
procexecl(nil, "/cmd/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;
static Lock shutdownlk;
killprocs();
for(i=0; oknotes[i]; i++)
if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0){
jehanne_lock(&shutdownlk); /* only one can threadexitsall */
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++)
if(window[i]->notefd >= 0)
2019-11-26 02:25:23 +01:00
jehanne_write(window[i]->notefd, "hangup", 6);
}
void
keyboardthread(void* _)
{
char *s;
threadsetname("keyboardthread");
while(s = recvp(kbdchan)){
if(*s == 'k' || *s == 'K')
shiftdown = utfrune(s+1, Kshift) != nil;
if(input == nil || sendp(input->ck, s) <= 0)
free(s);
}
}
int
inborder(Rectangle r, Point xy)
{
return ptinrect(xy, r) && !ptinrect(xy, insetrect(r, Selborder));
}
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+1, r.max.y);
break;
case 6: /* bottom left */
r = Rect(p.x, r.min.y, r.max.x, p.y+1);
break;
case 8: /* bottom right */
r = Rect(r.min.x, r.min.y, p.x+1, p.y+1);
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+1, r.max.y);
break;
case 7: /* bottom edge */
r = Rect(r.min.x, r.min.y, r.max.x, p.y+1);
break;
case 3: /* left edge */
r = Rect(p.x, r.min.y, r.max.x, r.max.y);
break;
}
return canonrect(r);
}
int
portion(int x, int lo, int hi)
{
x -= lo;
hi -= lo;
if(hi < 20)
return x > 0 ? 2 : 0;
if(x < 20)
return 0;
if(x > hi-20)
return 2;
return 1;
}
int
whichcorner(Rectangle r, Point p)
{
int i, j;
i = portion(p.x, r.min.x, r.max.x);
j = portion(p.y, r.min.y, r.max.y);
return 3*j+i;
}
/* 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);
flushimage(display, 1);
}
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;
Window *w, *winput;
Image *i;
Point xy;
Mouse tmp;
enum {
MReshape,
MMouse,
NALT
};
static Alt alts[NALT+1];
threadsetname("mousethread");
sending = FALSE;
scrolling = 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:
moving = FALSE;
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->deleted && 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 or less if window has been bottomed */
if(sending == FALSE && !scrolling && inborder(winput->screenr, mouse->xy) && winput->topped>0)
moving = TRUE;
else if(inside && (scrolling || winput->mouseopen || (mouse->buttons&1)))
sending = TRUE;
}else
sending = FALSE;
if(sending){
Sending:
wsetcursor(winput, FALSE);
if(mouse->buttons == 0)
sending = FALSE;
tmp = mousectl->Mouse;
tmp.xy = xy;
send(winput->mc.c, &tmp);
continue;
}
if(moving && (mouse->buttons&7)){
incref(&winput->ref);
sweeping = TRUE;
if(mouse->buttons & 3)
i = bandsize(winput);
else
i = drag(winput);
sweeping = FALSE;
if(i != nil)
wsendctlmesg(winput, Reshaped, i->r, i);
wclose(winput);
continue;
}
w = wpointto(mouse->xy);
if(w!=nil && inborder(w->screenr, mouse->xy))
riosetcursor(corners[whichcorner(w->screenr, mouse->xy)]);
else
wsetcursor(w, FALSE);
/* we're not sending the event, but if button is down maybe we should */
if(mouse->buttons){
/* w->topped will be zero or less 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->deleted && !winput->mouseopen){
incref(&winput->ref);
button2menu(winput);
wclose(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 || inborder(w->screenr, mouse->xy)))
goto Again;
goto Drain;
}
}
break;
Drain:
do
readmouse(mousectl);
while(mousectl->buttons);
goto Again; /* recalculate mouse position, cursor */
}
}
int
wtopcmp(const void *a, const void *b)
{
return (*(Window**)a)->topped - (*(Window**)b)->topped;
}
void
resized(void)
{
Image *im;
int i, j;
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, ZP);
o = subpt(viewr.max, viewr.min);
n = subpt(view->clipr.max, view->clipr.min);
qsort(window, nwindow, sizeof(window[0]), wtopcmp);
for(i=0; i<nwindow; i++){
w = window[i];
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;
r = rectaddpt(r, view->clipr.min);
if(!goodrect(r))
r = rectsubpt(w->i->r, subpt(w->i->r.min, r.min));
for(j=0; j<nhidden; j++)
if(w == hidden[j])
break;
incref(&w->ref);
if(j < nhidden){
im = allocimage(display, r, screen->chan, 0, DNofill);
r = ZR;
} else
im = allocwindow(wscreen, r, Refbackup, DNofill);
if(im)
wsendctlmesg(w, Reshaped, r, im);
wclose(w);
}
viewr = view->r;
flushimage(display, 1);
}
int
obscured(Window *w, Rectangle r, int i)
{
Window *t;
if(Dx(r) < font->height || Dy(r) < font->height)
return 1;
if(!rectclip(&r, screen->r))
return 1;
for(; i<nwindow; i++){
t = window[i];
if(t == w || t->topped <= w->topped)
continue;
if(Dx(t->screenr) == 0 || Dy(t->screenr) == 0 || rectXrect(r, t->screenr) == 0)
continue;
if(r.min.y < t->screenr.min.y)
if(!obscured(w, Rect(r.min.x, r.min.y, r.max.x, t->screenr.min.y), i))
return 0;
if(r.min.x < t->screenr.min.x)
if(!obscured(w, Rect(r.min.x, r.min.y, t->screenr.min.x, r.max.y), i))
return 0;
if(r.max.y > t->screenr.max.y)
if(!obscured(w, Rect(r.min.x, t->screenr.max.y, r.max.x, r.max.y), i))
return 0;
if(r.max.x > t->screenr.max.x)
if(!obscured(w, Rect(t->screenr.max.x, r.min.y, r.max.x, r.max.y), i))
return 0;
return 1;
}
return 0;
}
static char*
shortlabel(char *s)
{
enum { NBUF=60 };
static char buf[NBUF*UTFmax];
int i, k, l;
Rune r;
l = utflen(s);
if(l < NBUF-2)
return estrdup(s);
k = i = 0;
while(i < NBUF/2){
k += chartorune(&r, s+k);
i++;
}
strncpy(buf, s, k);
strcpy(buf+k, "...");
while((l-i) >= NBUF/2-4){
k += chartorune(&r, s+k);
i++;
}
strcat(buf, s+k);
return estrdup(buf);
}
void
button3menu(void)
{
int i, j, n;
n = nhidden;
for(i=0; i<nwindow; i++){
for(j=0; j<n; j++)
if(window[i] == hidden[j])
break;
if(j == n)
if(obscured(window[i], window[i]->screenr, 0)){
hidden[n++] = window[i];
if(n >= nelem(hidden))
break;
}
}
if(n >= nelem(menu3str)-Hidden)
n = nelem(menu3str)-Hidden-1;
for(i=0; i<n; i++){
free(menu3str[i+Hidden]);
menu3str[i+Hidden] = shortlabel(hidden[i]->label);
}
for(i+=Hidden; menu3str[i]; i++){
free(menu3str[i]);
menu3str[i] = nil;
}
sweeping = TRUE;
switch(i = menuhit(3, mousectl, &menu3, wscreen)){
case -1:
break;
case New:
new(sweep(), FALSE, scrolling, 0, nil, "/cmd/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 = FALSE;
}
void
button2menu(Window *w)
{
if(w->scrolling)
menu2str[Scroll] = "noscroll";
else
menu2str[Scroll] = "scroll";
switch(menuhit(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 Look:
wlook(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, (Rune*)L"\n", 1);
}else{
winsert(w, snarf, nsnarf, w->nr);
if(snarf[nsnarf-1]!='\n' && snarf[nsnarf-1]!='\004')
winsert(w, (Rune*)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;
}
flushimage(display, 1);
wsendctlmesg(w, Wakeup, ZR, nil);
}
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);
while(mouse->buttons == 0)
readmouse(mousectl);
p0 = onscreen(mouse->xy);
p = p0;
r.min = p;
r.max = p;
oi = nil;
while(mouse->buttons == 4){
if(!eqpt(mouse->xy, p)){
p = onscreen(mouse->xy);
r = canonrect(Rpt(p0, p));
r = whichrect(r, p, whichcorner(r, p));
if(Dx(r)>5 && Dy(r)>5){
i = allocwindow(wscreen, r, Refnone, DNofill);
freeimage(oi);
if(i == nil)
goto Rescue;
oi = i;
border(i, r, Selborder, sizecol, ZP);
draw(i, insetrect(r, Selborder), cols[BACK], nil, ZP);
}
}
readmouse(mousectl);
}
if(mouse->buttons != 0)
goto Rescue;
if(i==nil || !goodrect(r))
goto Rescue;
oi = i;
i = allocwindow(wscreen, oi->r, Refbackup, DNofill);
freeimage(oi);
if(i == nil)
goto Rescue;
riosetcursor(corners[whichcorner(i->r, mouse->xy)]);
goto Return;
Rescue:
riosetcursor(nil);
freeimage(i);
i = nil;
flushimage(display, 1);
while(mouse->buttons)
readmouse(mousectl);
Return:
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);
b = allocwindow(wscreen, r, Refbackup, DNofill);
if(b != nil) draw(b, r, sizecol, nil, ZP);
*bp = b;
}
}
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)
{
Point p, op, d, dm, om;
Rectangle r;
menuing = TRUE;
riosetcursor(&boxcursor);
om = mouse->xy;
dm = subpt(om, w->screenr.min);
d = subpt(w->screenr.max, w->screenr.min);
op = subpt(om, dm);
drawborder(Rect(op.x, op.y, op.x+d.x, op.y+d.y), 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);
op = p;
}
readmouse(mousectl);
}
r = Rect(op.x, op.y, op.x+d.x, op.y+d.y);
drawborder(r, 0);
p = mouse->xy;
riosetcursor(inborder(r, p) ? corners[whichcorner(r, p)] : nil);
menuing = FALSE;
if(mouse->buttons!=0 || !goodrect(r) || eqrect(r, w->screenr)){
flushimage(display, 1);
while(mouse->buttons)
readmouse(mousectl);
return nil;
}
return allocwindow(wscreen, r, Refbackup, DNofill);
}
Image*
bandsize(Window *w)
{
Rectangle r, or;
Point p, startp;
int which, but;
p = mouse->xy;
but = mouse->buttons;
which = whichcorner(w->screenr, p);
riosetcursor(corners[which]);
r = whichrect(w->screenr, p, which);
drawborder(r, 1);
or = r;
startp = p;
while(mouse->buttons==but){
p = onscreen(mouse->xy);
r = whichrect(w->screenr, p, which);
if(!eqrect(r, or) && goodrect(r)){
drawborder(r, 1);
or = r;
}
readmouse(mousectl);
}
p = mouse->xy;
drawborder(or, 0);
if(mouse->buttons!=0 || !goodrect(or) || eqrect(or, w->screenr)
|| abs(p.x-startp.x)+abs(p.y-startp.y) <= 1){
flushimage(display, 1);
while(mouse->buttons)
readmouse(mousectl);
return nil;
}
return allocwindow(wscreen, or, Refbackup, DNofill);
}
Window*
pointto(int wait)
{
Window *w;
menuing = TRUE;
riosetcursor(&sightcursor);
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 */
riosetcursor(nil);
w = nil;
}
readmouse(mousectl);
}
if(w != nil && wpointto(mouse->xy) != w)
w = nil;
}
riosetcursor(nil);
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;
incref(&w->ref);
i = sweep();
if(i)
wsendctlmesg(w, Reshaped, i->r, i);
wclose(w);
}
void
move(void)
{
Window *w;
Image *i;
w = pointto(FALSE);
if(w == nil)
return;
incref(&w->ref);
i = drag(w);
if(i)
wsendctlmesg(w, Reshaped, i->r, i);
wclose(w);
}
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;
incref(&w->ref);
i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
if(i){
hidden[nhidden++] = w;
wsendctlmesg(w, Reshaped, ZR, i);
}
wclose(w);
return i!=0;
}
int
wunhide(Window *w)
{
int j;
Image *i;
for(j=0; j<nhidden; j++)
if(hidden[j] == w)
break;
if(j == nhidden)
return -1; /* not hidden */
incref(&w->ref);
i = allocwindow(wscreen, w->i->r, Refbackup, DNofill);
if(i){
--nhidden;
memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
wsendctlmesg(w, Reshaped, w->i->r, i);
}
wclose(w);
return i!=0;
}
void
hide(void)
{
Window *w;
w = pointto(TRUE);
if(w)
whide(w);
}
void
unhide(int j)
{
Window *w;
if(j < Hidden)
return;
j -= Hidden;
w = hidden[j];
if(w == nil)
return;
if(j < nhidden){
wunhide(w);
return;
}
/* uncover obscured window */
for(j=0; j<nwindow; j++)
if(window[j] == w){
incref(&w->ref);
wtopme(w);
wcurrent(w);
wclose(w);
return;
}
}
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;
if(hideit && nhidden >= nelem(hidden)){
freeimage(i);
return nil;
}
cm = chancreate(sizeof(Mouse), 0);
ck = chancreate(sizeof(char*), 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);
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){
free(w->dir);
w->dir = estrdup(dir);
}
chanfree(cpid);
return w;
}
static void
kbdproc(void *arg)
{
Channel *c = arg;
char buf[128], *p, *e;
int fd, cfd, kfd, n;
threadsetname("kbdproc");
2019-11-26 02:25:23 +01:00
if((fd = sys_open("/dev/cons", OREAD)) < 0){
chanprint(c, "%r");
return;
}
2019-11-26 02:25:23 +01:00
if((cfd = sys_open("/dev/consctl", OWRITE)) < 0){
chanprint(c, "%r");
return;
}
fprint(cfd, "rawon");
if(sendp(c, nil) <= 0)
return;
2019-11-26 02:25:23 +01:00
if((kfd = sys_open("/dev/kbd", OREAD)) >= 0){
sys_close(fd);
/* only serve a kbd file per window when we got one */
servekbd = 1;
/* read kbd state */
2019-11-26 02:25:23 +01:00
while((n = jehanne_read(kfd, buf, sizeof(buf)-1)) > 0){
e = buf+n;
e[-1] = 0;
e[0] = 0;
for(p = buf; p < e; p += strlen(p)+1)
chanprint(c, "%s", p);
}
} else {
/* read single characters */
p = buf;
for(;;){
Rune r;
e = buf + sizeof(buf);
2019-11-26 02:25:23 +01:00
if((n = jehanne_read(fd, p, e-p)) <= 0)
break;
e = p + n;
while(p < e && fullrune(p, e - p)){
p += chartorune(&r, p);
if(r)
chanprint(c, "c%C", r);
}
n = e - p;
memmove(buf, p, n);
p = buf + n;
}
}
send(exitchan, nil);
}
Channel*
initkbd(void)
{
Channel *c;
char *e;
c = chancreate(sizeof(char*), 16);
procrfork(kbdproc, c, STACK, RFCFDG);
if(e = recvp(c)){
chanfree(c);
c = nil;
werrstr("%s", e);
free(e);
}
return c;
}