#include "dat.h"
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <control.h>
int ctldeletequits = 1;
typedef struct RequestType RequestType;
typedef struct Request Request;
typedef struct Memory Memory;
struct RequestType
{
char *file; /* file to read requests from */
void (*f)(Request*); /* request handler */
void (*r)(Controlset*); /* resize handler */
int fd; /* fd = open(file, ORDWR) */
Channel *rc; /* channel requests are multiplexed to */
Controlset *cs;
};
struct Request
{
RequestType *rt;
Attr *a;
Attr *tag;
};
struct Memory
{
Memory *next;
Attr *a;
Attr *val;
};
Memory *mem;
static void readreq(void*);
static void hide(void);
static void unhide(void);
static void openkmr(void);
static void closekmr(void);
static Memory* searchmem(Attr*);
static void addmem(Attr*, Attr*);
static void confirm(Request*);
static void resizeconfirm(Controlset*);
static void needkey(Request*);
static void resizeneedkey(Controlset*);
RequestType rt[] =
{
{ "/mnt/factotum/confirm", confirm, resizeconfirm, },
{ "/mnt/factotum/needkey", needkey, resizeneedkey, },
{ 0 },
};
enum
{
ButtonDim= 15,
};
void
threadmain(int argc, char *argv[])
{
Request r;
Channel *rc;
RequestType *p;
Font *invis;
ARGBEGIN{
}ARGEND;
fmtinstall('A', attrconv);
/* create the proc's that read */
rc = chancreate(sizeof(Request), 0);
for(p = rt; p->file != 0; p++){
p->fd = -1;
p->rc = rc;
proccreate(readreq, p, 16*1024);
}
/* gui initialization */
initdraw(0, 0, "factotum_gui");
initcontrols();
hide();
/* get an invisible font for passwords */
invis = openfont(display, "/lib/font/bit/lucm/passwd.9.font");
namectlfont(invis, "invisible");
/* serialize all requests */
for(;;){
if(recv(rc, &r) < 0)
break;
(*r.rt->f)(&r);
freeattr(r.a);
freeattr(r.tag);
}
threadexitsall(nil);
}
/*
* read requests and pass them to the main loop
*/
enum
{
Requestlen=4096,
};
static void
readreq(void *a)
{
RequestType *rt = a;
char *buf, *p;
int n;
Request r;
Attr **l;
rt->fd = open(rt->file, ORDWR);
if(rt->fd < 0)
sysfatal("opening %s: %r", rt->file);
rt->cs = nil;
buf = malloc(Requestlen);
if(buf == nil)
sysfatal("allocating read buffer: %r");
r.rt = rt;
for(;;){
n = read(rt->fd, buf, Requestlen-1);
if(n < 0)
break;
buf[n] = 0;
/* skip verb, parse attributes, and remove tag */
p = strchr(buf, ' ');
if(p != nil)
p++;
else
p = buf;
r.a = parseattr(p);
/* separate out the tag */
r.tag = nil;
for(l = &r.a; *l != nil; l = &(*l)->next)
if(strcmp(s_to_c((*l)->name), "tag") == 0){
r.tag = *l;
*l = r.tag->next;
r.tag->next = nil;
break;
}
/* if no tag, forget it */
if(r.tag == nil){
freeattr(r.a);
continue;
}
send(rt->rc, &r);
}
}
#ifdef asdf
static void
readreq(void *a)
{
RequestType *rt = a;
char *buf, *p;
int n;
Request r;
rt->fd = -1;
rt->cs = nil;
buf = malloc(Requestlen);
if(buf == nil)
sysfatal("allocating read buffer: %r");
r.rt = rt;
for(;;){
strcpy(buf, "adfasdf=afdasdf asdfasdf=asdfasdf");
r.a = parseattr(buf);
send(rt->rc, &r);
sleep(5000);
}
}
#endif asdf
/*
* open/close the keyboard, mouse and resize channels
*/
static Channel *kbdc;
static Channel *mousec;
static Channel *resizec;
static Keyboardctl *kctl;
static Mousectl *mctl;
static void
openkmr(void)
{
/* get channels for subsequent newcontrolset calls */
kctl = initkeyboard(nil);
if(kctl == nil)
sysfatal("can't initialize keyboard: %r");
kbdc = kctl->c;
mctl = initmouse(nil, screen);
if(mctl == nil)
sysfatal("can't initialize mouse: %r");
mousec = mctl->c;
resizec = mctl->resizec;
}
static void
closekmr(void)
{
Mouse m;
while(nbrecv(kbdc, &m) > 0)
;
closekeyboard(kctl);
while(nbrecv(mousec, &m) > 0)
;
closemouse(mctl);
}
/*
* called when the window is resized
*/
void
resizecontrolset(Controlset *cs)
{
RequestType *p;
for(p = rt; p->file != 0; p++){
if(p->cs == cs){
(*p->r)(cs);
break;
}
}
}
/*
* hide window when not in use
*/
static void
unhide(void)
{
int wctl;
wctl = open("/dev/wctl", OWRITE);
if(wctl < 0)
return;
fprint(wctl, "unhide");
close(wctl);
}
static void
hide(void)
{
int wctl;
int tries;
wctl = open("/dev/wctl", OWRITE);
if(wctl < 0)
return;
for(tries = 0; tries < 10; tries++){
if(fprint(wctl, "hide") >= 0)
break;
sleep(100);
}
close(wctl);
}
/* controls for confirm */
Control *msg;
Control *b_remember;
Control *t_remember;
Control *b_accept;
Control *b_refuse;
/*
* set up the controls for the confirmation window
*/
static Channel*
setupconfirm(Request *r)
{
Controlset *cs;
Channel *c;
Attr *a;
/* create a new control set for the confirmation */
openkmr();
cs = newcontrolset(screen, kbdc, mousec, resizec);
msg = createtext(cs, "msg");
chanprint(msg->ctl, "image paleyellow");
chanprint(msg->ctl, "border 1");
chanprint(msg->ctl, "add 'The following key is being used:'");
for(a = r->a; a != nil; a = a->next)
chanprint(msg->ctl, "add ' %s = %s'", s_to_c(a->name),
s_to_c(a->val));
namectlimage(display->white, "i_white");
namectlimage(display->black, "i_black");
b_remember = createbutton(cs, "b_remember");
chanprint(b_remember->ctl, "border 1");
chanprint(b_remember->ctl, "mask i_white");
chanprint(b_remember->ctl, "image i_white");
chanprint(b_remember->ctl, "light i_black");
t_remember = createtext(cs, "t_remember");
chanprint(t_remember->ctl, "image white");
chanprint(t_remember->ctl, "bordercolor white");
chanprint(t_remember->ctl, "add 'Memory this answer for future confirmations'");
b_accept = createtextbutton(cs, "b_accept");
chanprint(b_accept->ctl, "border 1");
chanprint(b_accept->ctl, "align center");
chanprint(b_accept->ctl, "text Accept");
chanprint(b_accept->ctl, "image i_white");
chanprint(b_accept->ctl, "light i_black");
b_refuse = createtextbutton(cs, "b_refuse");
chanprint(b_refuse->ctl, "border 1");
chanprint(b_refuse->ctl, "align center");
chanprint(b_refuse->ctl, "text Refuse");
chanprint(b_refuse->ctl, "image i_white");
chanprint(b_refuse->ctl, "light i_black");
c = chancreate(sizeof(char*), 0);
controlwire(b_remember, "event", c);
controlwire(b_accept, "event", c);
controlwire(b_refuse, "event", c);
/* make the controls interactive */
activate(b_remember);
activate(b_accept);
activate(b_refuse);
r->rt->cs = cs;
unhide();
resizecontrolset(cs);
return c;
}
/*
* resize the controls for the confirmation window
*/
static void
resizeconfirm(Controlset *)
{
Rectangle r, mr, nr, ntr, ar, rr;
int fontwidth;
fontwidth = font->height;
/* get usable window rectangle */
if(getwindow(display, Refnone) < 0)
ctlerror("resize failed: %r");
r = insetrect(screen->r, 10);
/* message box fills everything not needed for buttons */
mr = r;
mr.max.y = mr.min.y + font->height*((Dy(mr)-3*ButtonDim-font->height-4)/font->height);
/* remember button */
nr.min = Pt(mr.min.x, mr.max.y+ButtonDim);
nr.max = Pt(mr.max.x, r.max.y);
if(Dx(nr) > ButtonDim)
nr.max.x = nr.min.x+ButtonDim;
if(Dy(nr) > ButtonDim)
nr.max.y = nr.min.y+ButtonDim;
ntr.min = Pt(nr.max.x+ButtonDim, nr.min.y);
ntr.max = Pt(r.max.x, nr.min.y+font->height);
/* accept/refuse buttons */
ar.min = Pt(r.min.x+Dx(r)/2-ButtonDim-6*fontwidth, nr.max.y+ButtonDim);
ar.max = Pt(ar.min.x+6*fontwidth, ar.min.y+font->height+4);
rr.min = Pt(r.min.x+Dx(r)/2+ButtonDim, nr.max.y+ButtonDim);
rr.max = Pt(rr.min.x+6*fontwidth, rr.min.y+font->height+4);
/* make the controls visible */
chanprint(msg->ctl, "rect %R\nshow", mr);
chanprint(b_remember->ctl, "rect %R\nshow", nr);
chanprint(t_remember->ctl, "rect %R\nshow", ntr);
chanprint(b_accept->ctl, "rect %R\nshow", ar);
chanprint(b_refuse->ctl, "rect %R\nshow", rr);
}
/*
* free the controls for the confirmation window
*/
static void
teardownconfirm(Request *r)
{
Controlset *cs;
cs = r->rt->cs;
r->rt->cs = nil;
hide();
closecontrolset(cs);
closekmr();
}
/*
* get user confirmation of a key
*/
static void
confirm(Request *r)
{
Channel *c;
char *s;
int n;
char *args[3];
int remember;
Attr *val;
Memory *m;
/* if it's something that the user wanted us not to ask again about */
m = searchmem(r->a);
if(m != nil){
fprint(r->rt->fd, "%A %A", r->tag, m->val);
return;
}
/* set up the controls */
c = setupconfirm(r);
/* wait for user to reply */
remember = 0;
for(;;){
s = recvp(c);
n = tokenize(s, args, nelem(args));
if(n==3 && strcmp(args[1], "value")==0){
if(strcmp(args[0], "b_remember:") == 0){
remember = atoi(args[2]);
}
if(strcmp(args[0], "b_accept:") == 0){
val = mkattr(AttrNameval, "answer", "yes", nil);
free(s);
break;
}
if(strcmp(args[0], "b_refuse:") == 0){
val = mkattr(AttrNameval, "answer", "no", nil);
free(s);
break;
}
}
free(s);
}
teardownconfirm(r);
fprint(r->rt->fd, "%A %A", r->tag, val);
if(remember)
addmem(copyattr(r->a), val);
else
freeattr(val);
}
/*
* confirmations that are remembered
*/
static int
match(Attr *a, Attr *b)
{
Attr *x;
for(; a != nil; a = a->next){
x = findattr(b, s_to_c(a->name));
if(x == nil || strcmp(s_to_c(a->val), s_to_c(x->val)) != 0)
return 0;
}
return 1;
}
static Memory*
searchmem(Attr *a)
{
Memory *m;
for(m = mem; m != nil; m = m->next){
if(match(a, m->a))
break;
}
return m;
}
static void
addmem(Attr *a, Attr *val)
{
Memory *m;
m = malloc(sizeof *m);
if(m == nil)
return;
m->a = a;
m->val = val;
m->next = mem;
mem = m;
}
/* controls for needkey */
Control *msg;
Control *b_done;
enum {
Pprivate= 1<<0,
Pneed= 1<<1,
};
typedef struct Entry Entry;
struct Entry {
Control *name;
Control *val;
Attr *a;
};
static Entry *entry;
static int entries;
/*
* set up the controls for the confirmation window
*/
static Channel*
setupneedkey(Request *r)
{
Controlset *cs;
Channel *c;
Attr *a;
char cn[10];
int i;
Control *ctl;
/* create a new control set for the confirmation */
openkmr();
cs = newcontrolset(screen, kbdc, mousec, resizec);
/* count attributes and allocate entry controls */
entries = 0;
for(a = r->a; a != nil; a = a->next)
entries++;
if(entries == 0)
return nil;
entry = malloc(entries*sizeof(Entry));
if(entry == nil)
return nil;
namectlimage(display->white, "i_white");
namectlimage(display->black, "i_black");
/* create controls */
msg = createtext(cs, "msg");
chanprint(msg->ctl, "image white");
chanprint(msg->ctl, "bordercolor white");
chanprint(msg->ctl, "add 'You need the following key. Fill in the blanks'");
chanprint(msg->ctl, "add 'and click on the DONE button.'");
for(i = 0, a = r->a; a != nil; i++, a = a->next){
entry[i].a = a;
snprint(cn, sizeof cn, "name_%d", i);
ctl = entry[i].name = createtext(cs, cn);
chanprint(ctl->ctl, "image white");
chanprint(ctl->ctl, "bordercolor white");
chanprint(ctl->ctl, "add '%s ='", s_to_c(a->name));
snprint(cn, sizeof cn, "val_%d", i);
if(a->type == AttrQuery){
ctl = entry[i].val = createentry(cs, cn);
chanprint(ctl->ctl, "image yellow");
chanprint(ctl->ctl, "border 1");
if(strcmp(s_to_c(a->name), "user") == 0)
chanprint(ctl->ctl, "value %q", getuser());
if(*s_to_c(a->name) == '!')
chanprint(ctl->ctl, "font invisible");
} else {
ctl = entry[i].val = createtext(cs, cn);
chanprint(ctl->ctl, "image white");
chanprint(ctl->ctl, "bordercolor white");
chanprint(ctl->ctl, "add %q", s_to_c(a->val));
}
}
b_done = createtextbutton(cs, "b_done");
chanprint(b_done->ctl, "border 1");
chanprint(b_done->ctl, "align center");
chanprint(b_done->ctl, "text DONE");
chanprint(b_done->ctl, "image i_white");
chanprint(b_done->ctl, "light i_black");
/* wire controls for input */
c = chancreate(sizeof(char*), 0);
controlwire(b_done, "event", c);
for(i = 0; i < entries; i++)
if(entry[i].a->type == AttrQuery)
controlwire(entry[i].val, "event", c);
/* make the controls interactive */
activate(msg);
activate(b_done);
for(i = 0; i < entries; i++)
if(entry[i].a->type == AttrQuery)
activate(entry[i].val);
/* change the display */
r->rt->cs = cs;
unhide();
resizecontrolset(cs);
return c;
}
/*
* resize the controls for the confirmation window
*/
static void
resizeneedkey(Controlset *)
{
Rectangle r, mr;
int mid, i, n, lasty;
/* get usable window rectangle */
if(getwindow(display, Refnone) < 0)
ctlerror("resize failed: %r");
r = insetrect(screen->r, 10);
/* find largest name */
mid = 0;
for(i = 0; i < entries; i++){
n = s_len(entry[i].a->name);
if(n > mid)
mid = n;
}
mid = (mid+2) * font->height;
/* top line is the message */
mr = r;
mr.max.y = mr.min.y + 2*font->height + 2;
chanprint(msg->ctl, "rect %R\nshow", mr);
/* one line per attribute */
mr.min.x += 2*font->height;
lasty = mr.max.y;
for(i = 0; i < entries; i++){
r.min.x = mr.min.x;
r.min.y = lasty+2;
r.max.x = r.min.x + mid;
r.max.y = r.min.y + font->height;
chanprint(entry[i].name->ctl, "rect %R\nshow", r);
r.min.x = r.max.x;
r.max.x = mr.max.x;
if(Dx(r) > 32*font->height)
r.max.x = r.min.x + 32*font->height;
chanprint(entry[i].val->ctl, "rect %R\nshow", r);
lasty = r.max.y;
}
/* done button */
mr.min.x -= 2*font->height;
r.min.x = mr.min.x + mid - 3*font->height;
r.min.y = lasty+10;
r.max.x = r.min.x + 6*font->height;
r.max.y = r.min.y + font->height + 2;
chanprint(b_done->ctl, "rect %R\nshow", r);
}
/*
* free the controls for the confirmation window
*/
static void
teardownneedkey(Request *r)
{
Controlset *cs;
cs = r->rt->cs;
r->rt->cs = nil;
hide();
closecontrolset(cs);
closekmr();
if(entry != nil)
free(entry);
}
static void
needkey(Request *r)
{
Channel *c;
char *s;
int i, n;
int fd;
char *args[3];
/* set up the controls */
c = setupneedkey(r);
if(c == nil)
goto out;
/* wait for user to reply */
for(;;){
s = recvp(c);
n = tokenize(s, args, nelem(args));
if(n==3 && strcmp(args[1], "value")==0){
if(strcmp(args[0], "b_done:") == 0){
free(s);
break;
}
}
free(s);
}
/* get entry values */
for(i = 0; i < entries; i++){
if(entry[i].a->type != AttrQuery)
continue;
chanprint(entry[i].val->ctl, "data");
s = recvp(entry[i].val->data);
if(s != nil){
entry[i].a->val = s_copy(s);
free(s);
}
entry[i].a->type = AttrNameval;
}
/* enter the new key !!!!need to do something in case of error!!!! */
fd = open("/mnt/factotum/ctl", OWRITE);
fprint(fd, "key %A", r->a);
close(fd);
out:
teardownneedkey(r);
fprint(r->rt->fd, "%A", r->tag);
}
syntax highlighted by Code2HTML, v. 0.9.1