ref: db0eab9ca04b896e5760081efd854c57f65f36c3
dir: /libtk/ebind.c/
#include "lib9.h"
#include "draw.h"
#include "tk.h"
#include <kernel.h>
#include <interp.h>
enum
{
Cmask,
Cctl,
Ckey,
Cbp,
Cbr,
};
struct
{
char* event;
int mask;
int action;
} etab[] =
{
"Motion", TkMotion, Cmask,
"Double", TkDouble, Cmask,
"Map", TkMap, Cmask,
"Unmap", TkUnmap, Cmask,
"Destroy", TkDestroy, Cmask,
"Enter", TkEnter, Cmask,
"Leave", TkLeave, Cmask,
"FocusIn", TkFocusin, Cmask,
"FocusOut", TkFocusout, Cmask,
"Configure", TkConfigure, Cmask,
"Control", 0, Cctl,
"Key", 0, Ckey,
"KeyPress", 0, Ckey,
"Button", 0, Cbp,
"ButtonPress", 0, Cbp,
"ButtonRelease", 0, Cbr,
};
static
TkOption tkcurop[] =
{
"x", OPTdist, O(TkCursor, p.x), nil,
"y", OPTdist, O(TkCursor, p.y), nil,
"bitmap", OPTbmap, O(TkCursor, bit), nil,
"image", OPTimag, O(TkCursor, img), nil,
"default", OPTbool, O(TkCursor, def), nil,
nil
};
static
TkOption focusopts[] = {
"global", OPTbool, 0, nil,
nil
};
static char*
tkseqitem(char *buf, char *arg)
{
while(*arg && (*arg == ' ' || *arg == '-'))
arg++;
while(*arg && *arg != ' ' && *arg != '-' && *arg != '>')
*buf++ = *arg++;
*buf = '\0';
return arg;
}
static char*
tkseqkey(Rune *r, char *arg)
{
char *narg;
while(*arg && (*arg == ' ' || *arg == '-'))
arg++;
if (*arg == '\\') {
if (*++arg == '\0') {
*r = 0;
return arg;
}
} else if (*arg == '\0' || *arg == '>' || *arg == '-') {
*r = 0;
return arg;
}
narg = arg + chartorune(r, arg);
return narg;
}
int
tkseqparse(char *seq)
{
Rune r;
int i, event;
char *buf;
buf = mallocz(Tkmaxitem, 0);
if(buf == nil)
return -1;
event = 0;
while(*seq && *seq != '>') {
seq = tkseqitem(buf, seq);
for(i = 0; i < nelem(etab); i++)
if(strcmp(buf, etab[i].event) == 0)
break;
if(i >= nelem(etab)) {
seq = tkextnparseseq(buf, seq, &event);
if (seq == nil) {
free(buf);
return -1;
}
continue;
}
switch(etab[i].action) {
case Cmask:
event |= etab[i].mask;
break;
case Cctl:
seq = tkseqkey(&r, seq);
if(r == 0) {
free(buf);
return -1;
}
if(r <= '~')
r &= 0x1f;
event |= TkKey|TKKEY(r);
break;
case Ckey:
seq = tkseqkey(&r, seq);
if(r != 0)
event |= TKKEY(r);
event |= TkKey;
break;
case Cbp:
seq = tkseqitem(buf, seq);
switch(buf[0]) {
default:
free(buf);
return -1;
case '\0':
event |= TkEpress;
break;
case '1':
event |= TkButton1P;
break;
case '2':
event |= TkButton2P;
break;
case '3':
event |= TkButton3P;
break;
case '4':
event |= TkButton4P;
break;
case '5':
event |= TkButton5P;
break;
case '6':
event |= TkButton6P;
break;
}
break;
case Cbr:
seq = tkseqitem(buf, seq);
switch(buf[0]) {
default:
free(buf);
return -1;
case '\0':
event |= TkErelease;
break;
case '1':
event |= TkButton1R;
break;
case '2':
event |= TkButton2R;
break;
case '3':
event |= TkButton3R;
break;
case '4':
event |= TkButton4R;
break;
case '5':
event |= TkButton5R;
break;
case '6':
event |= TkButton6R;
break;
}
break;
}
}
free(buf);
return event;
}
void
tkcmdbind(Tk *tk, int event, char *s, void *data)
{
Point p;
TkMouse *m;
TkGeom *g;
int v, len;
char *e, *c, *ec, *cmd;
TkTop *t;
if(s == nil)
return;
cmd = malloc(2*Tkmaxitem);
if (cmd == nil) {
print("tk: bind command \"%s\": %s\n",
tk->name ? tk->name->name : "(noname)", TkNomem);
return;
}
m = (TkMouse*)data;
c = cmd;
ec = cmd+2*Tkmaxitem-1;
while(*s && c < ec) {
if(*s != '%') {
*c++ = *s++;
continue;
}
s++;
len = ec-c;
switch(*s++) {
def:
default:
*c++ = s[-1];
break;
case '%':
*c++ = '%';
break;
case 'b':
v = 0;
if (!(event & TkKey)) {
if(event & (TkButton1P|TkButton1R))
v = 1;
else if(event & (TkButton2P|TkButton2R))
v = 2;
else if(event & (TkButton3P|TkButton3R))
v = 3;
}
c += snprint(c, len, "%d", v);
break;
case 'h':
if((event & TkConfigure) == 0)
goto def;
g = (TkGeom*)data;
c += snprint(c, len, "%d", g->height);
break;
case 's':
if((event & TkKey))
c += snprint(c, len, "%d", TKKEY(event));
else if((event & (TkEmouse|TkEnter)))
c += snprint(c, len, "%d", m->b);
else if((event & TkFocusin))
c += snprint(c, len, "%d", (int)data);
else
goto def;
break;
case 'w':
if((event & TkConfigure) == 0)
goto def;
g = (TkGeom*)data;
c += snprint(c, len, "%d", g->width);
break;
case 'x': /* Relative mouse coords */
case 'y':
if((event & TkKey) || (event & (TkEmouse|TkEnter)) == 0)
goto def;
p = tkposn(tk);
if(s[-1] == 'x')
v = m->x - p.x;
else
v = m->y - p.y;
c += snprint(c, len, "%d", v - tk->borderwidth);
break;
case 'X': /* Absolute mouse coords */
case 'Y':
if((event & TkKey) || (event & TkEmouse) == 0)
goto def;
c += snprint(c, len, "%d", s[-1] == 'X' ? m->x : m->y);
break;
case 'A':
if((event & TkKey) == 0)
goto def;
v = TKKEY(event);
if(v == '{' || v == '}' || v == '\\')
c += snprint(c, len, "\\%C", v);
else if(v != '\0')
c += snprint(c, len, "%C", v);
break;
case 'K':
if((event & TkKey) == 0)
goto def;
c += snprint(c, len, "%.4X", TKKEY(event));
break;
case 'W':
if (tk->name != nil)
c += snprint(c, len, "%s", tk->name->name);
break;
}
}
*c = '\0';
e = nil;
t = tk->env->top;
t->execdepth = 0;
if(cmd[0] == '|')
tkexec(t, cmd+1, nil);
else if(cmd[0] != '\0')
e = tkexec(t, cmd, nil);
t->execdepth = -1;
if(e == nil) {
free(cmd);
return;
}
if(tk->name != nil){
char *s;
if(t->errx[0] != '\0')
s = tkerrstr(t, e);
else
s = e;
print("tk: bind command \"%s\": %s: %s\n", tk->name->name, cmd, s);
if(s != e)
free(s);
}
free(cmd);
}
char*
tkbind(TkTop *t, char *arg, char **ret)
{
Rune r;
Tk *tk;
TkAction **ap;
int i, mode, event;
char *cmd, *tag, *seq;
char *e;
USED(ret);
tag = mallocz(Tkmaxitem, 0);
if(tag == nil)
return TkNomem;
seq = mallocz(Tkmaxitem, 0);
if(seq == nil) {
free(tag);
return TkNomem;
}
arg = tkword(t, arg, tag, tag+Tkmaxitem, nil);
if(tag[0] == '\0') {
e = TkBadtg;
goto err;
}
arg = tkword(t, arg, seq, seq+Tkmaxitem, nil);
if(seq[0] == '<') {
event = tkseqparse(seq+1);
if(event == -1) {
e = TkBadsq;
goto err;
}
}
else {
chartorune(&r, seq);
event = TkKey | r;
}
if(event == 0) {
e = TkBadsq;
goto err;
}
arg = tkskip(arg, " \t");
mode = TkArepl;
if(*arg == '+') {
mode = TkAadd;
arg++;
}
else if(*arg == '-'){
mode = TkAsub;
arg++;
}
if(*arg == '{') {
cmd = tkskip(arg+1, " \t");
if(*cmd == '}') {
tk = tklook(t, tag, 0);
if(tk == nil) {
for(i = 0; ; i++) {
if(i >= TKwidgets) {
e = TkBadwp;
tkerr(t, tag);
goto err;
}
if(strcmp(tag, tkmethod[i]->name) == 0) {
ap = &(t->binds[i]);
break;
}
}
}
else
ap = &tk->binds;
tkcancel(ap, event);
}
}
tkword(t, arg, seq, seq+Tkmaxitem, nil);
if(tag[0] == '.') {
tk = tklook(t, tag, 0);
if(tk == nil) {
e = TkBadwp;
tkerr(t, tag);
goto err;
}
cmd = strdup(seq);
if(cmd == nil) {
e = TkNomem;
goto err;
}
e = tkaction(&tk->binds, event, TkDynamic, cmd, mode);
if(e != nil)
goto err; /* tkaction does free(cmd) */
free(tag);
free(seq);
return nil;
}
/* documented but doesn't work */
if(strcmp(tag, "all") == 0) {
for(tk = t->root; tk; tk = tk->next) {
cmd = strdup(seq);
if(cmd == nil) {
e = TkNomem;
goto err;
}
e = tkaction(&tk->binds, event, TkDynamic, cmd, mode);
if(e != nil)
goto err;
}
free(tag);
free(seq);
return nil;
}
/* undocumented, probably unused, and doesn't work consistently */
for(i = 0; i < TKwidgets; i++) {
if(strcmp(tag, tkmethod[i]->name) == 0) {
cmd = strdup(seq);
if(cmd == nil) {
e = TkNomem;
goto err;
}
e = tkaction(t->binds + i,event, TkDynamic, cmd, mode);
if(e != nil)
goto err;
free(tag);
free(seq);
return nil;
}
}
e = TkBadtg;
err:
free(tag);
free(seq);
return e;
}
char*
tksend(TkTop *t, char *arg, char **ret)
{
TkVar *v;
char *var;
USED(ret);
var = mallocz(Tkmaxitem, 0);
if(var == nil)
return TkNomem;
arg = tkword(t, arg, var, var+Tkmaxitem, nil);
v = tkmkvar(t, var, 0);
free(var);
if(v == nil)
return TkBadvr;
if(v->type != TkVchan)
return TkNotvt;
arg = tkskip(arg, " \t");
if(tktolimbo(v->value, arg) == 0)
return TkMovfw;
return nil;
}
static Tk*
tknextfocus(TkTop *t, int d)
{
int i, n, j, k;
Tk *oldfocus;
if (t->focusorder == nil)
tkbuildfocusorder(t);
oldfocus = t->ctxt->tkkeygrab;
n = t->nfocus;
if (n == 0)
return oldfocus;
for (i = 0; i < n; i++)
if (t->focusorder[i] == oldfocus)
break;
if (i == n) {
for (i = 0; i < n; i++)
if ((t->focusorder[i]->flag & Tkdisabled) == 0)
return t->focusorder[i];
return oldfocus;
}
for (j = 1; j < n; j++) {
k = (i + d * j + n) % n;
if ((t->focusorder[k]->flag & Tkdisabled) == 0)
return t->focusorder[k];
}
return oldfocus;
}
/* our dirty little secret */
static void
focusdirty(Tk *tk)
{
if(tk->highlightwidth > 0){
tk->dirty = tkrect(tk, 1);
tkdirty(tk);
}
}
void
tksetkeyfocus(TkTop *top, Tk *new, int dir)
{
TkCtxt *c;
Tk *old;
c = top->ctxt;
old = c->tkkeygrab;
if(old == new)
return;
c->tkkeygrab = new;
if(top->focused == 0)
return;
if(old != nil && old != top->root){
tkdeliver(old, TkFocusout, nil);
focusdirty(old);
}
if(new != nil && new != top->root){
tkdeliver(new, TkFocusin, (void*)dir);
focusdirty(new);
}
}
void
tksetglobalfocus(TkTop *top, int in)
{
Tk *tk;
in = (in != 0);
if (in != top->focused){
top->focused = in;
tk = top->ctxt->tkkeygrab;
if(in){
tkdeliver(top->root, TkFocusin, (void*)0);
if(tk != nil && tk != top->root){
tkdeliver(tk, TkFocusin, (void*)0);
focusdirty(tk);
}
}else{
if(tk != nil && tk != top->root){
tkdeliver(tk, TkFocusout, nil);
focusdirty(tk);
}
tkdeliver(top->root, TkFocusout, nil);
}
}
}
char*
tkfocus(TkTop *top, char *arg, char **ret)
{
Tk *tk;
char *wp, *e;
int dir, global;
TkOptab tko[2];
TkName *names;
tko[0].ptr = &global;
tko[0].optab = focusopts;
tko[1].ptr = nil;
global = 0;
names = nil;
e = tkparse(top, arg, tko, &names);
if (e != nil)
return e;
if(names == nil){
if(global)
return tkvalue(ret, "%d", top->focused);
tk = top->ctxt->tkkeygrab;
if (tk != nil && tk->name != nil)
return tkvalue(ret, "%s", tk->name->name);
return nil;
}
if(global){
tksetglobalfocus(top, atoi(names->name));
return nil;
}
wp = mallocz(Tkmaxitem, 0);
if(wp == nil)
return TkNomem;
tkword(top, arg, wp, wp+Tkmaxitem, nil);
if (!strcmp(wp, "next")) {
tk = tknextfocus(top, 1); /* can only return nil if c->tkkeygrab is already nil */
dir = +1;
} else if (!strcmp(wp, "previous")) {
tk = tknextfocus(top, -1);
dir = -1;
} else if(*wp == '\0') {
tk = nil;
dir = 0;
} else {
tk = tklook(top, wp, 0);
if(tk == nil){
tkerr(top, wp);
free(wp);
return TkBadwp;
}
dir = 0;
}
free(wp);
tksetkeyfocus(top, tk, dir);
return nil;
}
char*
tkraise(TkTop *t, char *arg, char **ret)
{
Tk *tk;
char *wp;
USED(ret);
wp = mallocz(Tkmaxitem, 0);
if(wp == nil)
return TkNomem;
tkword(t, arg, wp, wp+Tkmaxitem, nil);
tk = tklook(t, wp, 0);
if(tk == nil){
tkerr(t, wp);
free(wp);
return TkBadwp;
}
free(wp);
if((tk->flag & Tkwindow) == 0)
return TkNotwm;
tkwreq(tk->env->top, "raise %s", tk->name->name);
return nil;
}
char*
tklower(TkTop *t, char *arg, char **ret)
{
Tk *tk;
char *wp;
USED(ret);
wp = mallocz(Tkmaxitem, 0);
if(wp == nil)
return TkNomem;
tkword(t, arg, wp, wp+Tkmaxitem, nil);
tk = tklook(t, wp, 0);
if(tk == nil){
tkerr(t, wp);
free(wp);
return TkBadwp;
}
free(wp);
if((tk->flag & Tkwindow) == 0)
return TkNotwm;
tkwreq(tk->env->top, "lower %s", tk->name->name);
return nil;
}
char*
tkgrab(TkTop *t, char *arg, char **ret)
{
Tk *tk;
TkCtxt *c;
char *r, *buf, *wp;
USED(ret);
buf = mallocz(Tkmaxitem, 0);
if(buf == nil)
return TkNomem;
wp = mallocz(Tkmaxitem, 0);
if(wp == nil) {
free(buf);
return TkNomem;
}
arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
tkword(t, arg, wp, wp+Tkmaxitem, nil);
tk = tklook(t, wp, 0);
if(tk == nil) {
free(buf);
tkerr(t, wp);
free(wp);
return TkBadwp;
}
free(wp);
c = t->ctxt;
if(strcmp(buf, "release") == 0) {
free(buf);
if(c->mgrab == tk)
tksetmgrab(t, nil);
return nil;
}
if(strcmp(buf, "set") == 0) {
free(buf);
return tksetmgrab(t, tk);
}
if(strcmp(buf, "ifunset") == 0) {
free(buf);
if(c->mgrab == nil)
return tksetmgrab(t, tk);
return nil;
}
if(strcmp(buf, "status") == 0) {
free(buf);
r = "none";
if ((c->mgrab != nil) && (c->mgrab->name != nil))
r = c->mgrab->name->name;
return tkvalue(ret, "%s", r);
}
free(buf);
return TkBadcm;
}
char*
tkputs(TkTop *t, char *arg, char **ret)
{
char *buf;
USED(ret);
buf = mallocz(Tkmaxitem, 0);
if(buf == nil)
return TkNomem;
tkword(t, arg, buf, buf+Tkmaxitem, nil);
print("%s\n", buf);
free(buf);
return nil;
}
char*
tkdestroy(TkTop *t, char *arg, char **ret)
{
int found, len, isroot;
Tk *tk, **l, *next, *slave;
char *n, *e, *buf;
USED(ret);
buf = mallocz(Tkmaxitem, 0);
if(buf == nil)
return TkNomem;
e = nil;
for(;;) {
arg = tkword(t, arg, buf, buf+Tkmaxitem, nil);
if(buf[0] == '\0')
break;
len = strlen(buf);
found = 0;
isroot = (strcmp(buf, ".") == 0);
for(tk = t->root; tk; tk = tk->siblings) {
if (tk->name != nil) {
n = tk->name->name;
if(strcmp(buf, n) == 0) {
tk->flag |= Tkdestroy;
found = 1;
} else if(isroot || (strncmp(buf, n, len) == 0 && n[len] == '.'))
tk->flag |= Tkdestroy;
}
}
if(!found) {
e = TkBadwp;
tkerr(t, buf);
break;
}
}
free(buf);
for(tk = t->root; tk; tk = tk->siblings) {
if((tk->flag & Tkdestroy) == 0)
continue;
if(tk->flag & Tkwindow) {
tkunmap(tk);
if(tk->name != nil &&
strcmp(tk->name->name, ".") == 0)
tk->flag &= ~Tkdestroy;
else
tkdeliver(tk, TkDestroy, nil);
} else
tkdeliver(tk, TkDestroy, nil);
if(0)print("tkdestroy %q\n", tkname(tk));
if(tk->destroyed != nil)
tk->destroyed(tk);
tkpackqit(tk->master);
tkdelpack(tk);
for (slave = tk->slave; slave != nil; slave = next) {
next = slave->next;
slave->master = nil;
slave->next = nil;
}
tk->slave = nil;
if(tk->parent != nil && tk->geom != nil) /* XXX this appears to be bogus */
tk->geom(tk, 0, 0, 0, 0);
if(tk->grid){
tkfreegrid(tk->grid);
tk->grid = nil;
}
}
tkrunpack(t);
l = &t->windows;
for(tk = t->windows; tk; tk = next) {
next = TKobj(TkWin, tk)->next;
if(tk->flag & Tkdestroy) {
*l = next;
continue;
}
l = &TKobj(TkWin, tk)->next;
}
l = &t->root;
for(tk = t->root; tk; tk = next) {
next = tk->siblings;
if(tk->flag & Tkdestroy) {
*l = next;
tkfreeobj(tk);
continue;
}
l = &tk->siblings;
}
return e;
}
char*
tkupdatecmd(TkTop *t, char *arg, char **ret)
{
Tk *tk;
int x, y;
Rectangle *dr;
char buf[Tkmaxitem];
USED(ret);
tkword(t, arg, buf, buf+sizeof(buf), nil);
if(strcmp(buf, "-onscreen") == 0){
tk = t->root;
dr = &t->screenr;
x = tk->act.x;
if(x+tk->act.width > dr->max.x)
x = dr->max.x - tk->act.width;
if(x < 0)
x = 0;
y = tk->act.y;
if(y+tk->act.height > dr->max.y)
y = dr->max.y - tk->act.height;
if(y < 0)
y = 0;
tkmovewin(tk, Pt(x, y));
}else if(strcmp(buf, "-disable") == 0){
t->noupdate = 1;
}else if(strcmp(buf, "-enable") == 0){
t->noupdate = 0;
}
return tkupdate(t);
}
char*
tkwinfo(TkTop *t, char *arg, char **ret)
{
Tk *tk;
char *cmd, *arg1;
cmd = mallocz(Tkmaxitem, 0);
if(cmd == nil)
return TkNomem;
arg = tkword(t, arg, cmd, cmd+Tkmaxitem, nil);
if(strcmp(cmd, "class") == 0) {
arg1 = mallocz(Tkmaxitem, 0);
if(arg1 == nil) {
free(cmd);
return TkNomem;
}
tkword(t, arg, arg1, arg1+Tkmaxitem, nil);
tk = tklook(t, arg1, 0);
if(tk == nil){
tkerr(t, arg1);
free(arg1);
free(cmd);
return TkBadwp;
}
free(arg1);
free(cmd);
return tkvalue(ret, "%s", tkmethod[tk->type]->name);
}
free(cmd);
return TkBadvl;
}
char*
tkcursorcmd(TkTop *t, char *arg, char **ret)
{
char *e;
int locked;
Display *d;
TkCursor c;
TkOptab tko[3];
enum {Notset = 0x80000000};
c.def = 0;
c.p.x = Notset;
c.p.y = Notset;
c.bit = nil;
c.img = nil;
USED(ret);
c.def = 0;
tko[0].ptr = &c;
tko[0].optab = tkcurop;
tko[1].ptr = nil;
e = tkparse(t, arg, tko, nil);
if(e != nil)
return e;
d = t->display;
locked = lockdisplay(d);
if(c.def)
tkcursorswitch(t, nil, nil);
if(c.img != nil || c.bit != nil){
e = tkcursorswitch(t, c.bit, c.img);
tkimgput(c.img);
freeimage(c.bit);
}
if(e == nil){
if(c.p.x != Notset && c.p.y != Notset)
tkcursorset(t, c.p);
}
if(locked)
unlockdisplay(d);
return e;
}
char *
tkbindings(TkTop *t, Tk *tk, TkEbind *b, int blen)
{
TkAction *a, **ap;
char *cmd, *e;
int i;
e = nil;
for(i = 0; e == nil && i < blen; i++) /* default bindings */ {
int how = TkArepl;
char *cmd = b[i].cmd;
if(cmd[0] == '+') {
how = TkAadd;
cmd++;
}
else if(cmd[0] == '-'){
how = TkAsub;
cmd++;
}
e = tkaction(&tk->binds, b[i].event, TkStatic, cmd, how);
}
if(e != nil)
return e;
ap = &tk->binds;
for(a = t->binds[tk->type]; a; a = a->link) { /* user "defaults" */
cmd = strdup(a->arg);
if(cmd == nil)
return TkNomem;
e = tkaction(ap, a->event, TkDynamic, cmd,
(a->type >> 8) & 0xff);
if(e != nil)
return e;
ap = &(*ap)->link;
}
return nil;
}