537 lines
8.8 KiB
C
537 lines
8.8 KiB
C
|
#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;
|
||
|
/* reasonable sizes only please */
|
||
|
if(Dx(r) > BIG*Dx(screen->r))
|
||
|
return 0;
|
||
|
if(Dy(r) > BIG*Dx(screen->r))
|
||
|
return 0;
|
||
|
if(Dx(r) < 100 || Dy(r) < 3*font->height)
|
||
|
return 0;
|
||
|
/* window must be on screen */
|
||
|
if(!rectXrect(screen->r, r))
|
||
|
return 0;
|
||
|
/* must have some screen and border visible so we can move it out of the way */
|
||
|
if(rectinrect(screen->r, insetrect(r, Borderwidth)))
|
||
|
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(*maxp > max){
|
||
|
*minp += max-*maxp;
|
||
|
*maxp = max;
|
||
|
}
|
||
|
if(*minp < min){
|
||
|
*maxp += min-*minp;
|
||
|
if(*maxp > max)
|
||
|
*maxp = max;
|
||
|
*minp = min;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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, DNofill);
|
||
|
else
|
||
|
i = allocwindow(wscreen, rect, Refbackup, DNofill);
|
||
|
if(i == nil){
|
||
|
strcpy(err, Ewalloc);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
new(i, hideit, scrollit, pid, dir, "/cmd/rc", argv);
|
||
|
|
||
|
free(argv); /* when new() returns, argv and args have been copied */
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
wctlcmd(Window *w, Rectangle r, int cmd, char *err)
|
||
|
{
|
||
|
Image *i;
|
||
|
|
||
|
switch(cmd){
|
||
|
case Move:
|
||
|
r = Rect(r.min.x, r.min.y, r.min.x+Dx(w->screenr), r.min.y+Dy(w->screenr));
|
||
|
r = rectonscreen(r);
|
||
|
/* fall through */
|
||
|
case Resize:
|
||
|
if(!goodrect(r)){
|
||
|
strcpy(err, Ebadwr);
|
||
|
return -1;
|
||
|
}
|
||
|
if(Dx(w->screenr) > 0){
|
||
|
if(eqrect(r, w->screenr))
|
||
|
return 1;
|
||
|
if(w != input){
|
||
|
strcpy(err, "window not current");
|
||
|
return -1;
|
||
|
}
|
||
|
i = allocwindow(wscreen, r, Refbackup, DNofill);
|
||
|
} else { /* hidden */
|
||
|
if(eqrect(r, w->i->r))
|
||
|
return 1;
|
||
|
i = allocimage(display, r, w->i->chan, 0, DNofill);
|
||
|
r = ZR;
|
||
|
}
|
||
|
if(i == nil){
|
||
|
strcpy(err, Ewalloc);
|
||
|
return -1;
|
||
|
}
|
||
|
wsendctlmesg(w, Reshaped, 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:
|
||
|
if(Dx(w->screenr)<=0){
|
||
|
strcpy(err, "window is hidden");
|
||
|
return -1;
|
||
|
}
|
||
|
wtopme(w);
|
||
|
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:
|
||
|
switch(wunhide(w)){
|
||
|
case -1:
|
||
|
strcpy(err, "window not hidden");
|
||
|
return -1;
|
||
|
case 0:
|
||
|
strcpy(err, "hide failed");
|
||
|
return -1;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return 1;
|
||
|
case Delete:
|
||
|
wsendctlmesg(w, Deleted, ZR, nil);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
strcpy(err, "invalid wctl message");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
writewctl(Xfid *x, char *err)
|
||
|
{
|
||
|
int cnt, cmd, id, hideit, scrollit, pid;
|
||
|
char *arg, *dir;
|
||
|
Rectangle r;
|
||
|
Window *w;
|
||
|
|
||
|
w = x->f->w;
|
||
|
cnt = x->count;
|
||
|
x->data[cnt] = '\0';
|
||
|
id = 0;
|
||
|
|
||
|
r = rectsubpt(w->screenr, screen->r.min);
|
||
|
cmd = parsewctl(&arg, r, &r, &pid, &id, &hideit, &scrollit, &dir, x->data, err);
|
||
|
if(cmd < 0)
|
||
|
return -1;
|
||
|
|
||
|
if(id != 0){
|
||
|
w = wlookid(id);
|
||
|
if(w == 0){
|
||
|
strcpy(err, "no such window id");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(cmd){
|
||
|
case New:
|
||
|
return wctlnew(r, arg, pid, hideit, scrollit, dir, err);
|
||
|
case Set:
|
||
|
if(pid > 0)
|
||
|
wsetpid(w, pid, 0);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
incref(&w->ref);
|
||
|
id = wctlcmd(w, r, cmd, err);
|
||
|
wclose(w);
|
||
|
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|