home *** CD-ROM | disk | FTP | other *** search
- /* keys.c -- Key binding and evaluating
- Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
-
- This file is part of Jade.
-
- Jade is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- Jade is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Jade; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "jade.h"
- #include "jade_protos.h"
-
- #include <string.h>
-
- _PR VALUE usekey(void *, Event *, bool);
- _PR void keymap_sweep(void);
- _PR void keymap_prin(VALUE, VALUE);
- _PR void keys_init(void);
- _PR void keys_kill(void);
-
- _PR Event *CurrentEvent;
- Event *CurrentEvent;
-
- Keytab *KeytabChain;
- Keylist *KeylistChain;
-
- static VALUE sym_keymap_path, sym_unbound_key_hook, NextKeymapPath;
-
- /*
- ::doc:keymap_path::
- A list of keymaps (ie, keylists and/or keytables). When an event occurs
- each keymap in the list is searched for an event binding which matches
- it. These bindings are installed in a keymap by the function `bind-keys'.
- See also `next-keymap-path'.
- ::end::
- ::doc:unbound_key_hook::
- When no event binding can be found for an event this hook is evaluated in
- the standard manner (see the function `eval-hook' for details).
- ::end::
- */
-
- static Key *
- findkey(VALUE v, EventDef *evd)
- {
- Key *nxt, *k = NULL;
- switch(VTYPE(v))
- {
- case V_Keytab:
- k = VKEYTAB(v)->kt_Keys[evd->evd_Code & EV_HASH_MASK];
- while(k)
- {
- if((k->ky_Event.evd_Type == evd->evd_Type)
- && (k->ky_Event.evd_Mods == evd->evd_Mods)
- && (k->ky_Event.evd_Code == evd->evd_Code))
- break;
- k = k->ky_Link.next;
- }
- break;
- case V_Keylist:
- k = (Key *)VKEYLIST(v)->kl_List.mlh_Head;
- while((nxt = (Key *)k->ky_Link.node.mln_Succ))
- {
- if((k->ky_Event.evd_Type == evd->evd_Type)
- && (k->ky_Event.evd_Mods == evd->evd_Mods)
- && (k->ky_Event.evd_Code == evd->evd_Code))
- break;
- k = nxt;
- }
- if(!k->ky_Link.node.mln_Succ)
- k = NULL;
- break;
- }
- return(k);
- }
-
- static VALUE
- evalbinding(EventDef *evd)
- {
- VALUE kp;
- if(!NILP(NextKeymapPath))
- {
- kp = NextKeymapPath;
- NextKeymapPath = sym_nil;
- }
- else
- {
- if(!(kp = cmd_symbol_value(sym_keymap_path)))
- return(NULL);
- }
- while(CONSP(kp))
- {
- VALUE thispath = VCAR(kp);
- Key *k;
- switch(VTYPE(thispath))
- {
- case V_Symbol:
- if(!(thispath = cmd_symbol_value(thispath))
- || (!KEYTABP(thispath) && !KEYLISTP(thispath)))
- break;
- /* FALL THROUGH */
- case V_Keytab:
- case V_Keylist:
- k = findkey(thispath, evd);
- if(k)
- return(k->ky_Form);
- break;
- }
- kp = VCDR(kp);
- }
- return(NULL);
- }
-
- /*
- * `OSInputMsg' is the raw input event from the window-system, this is
- * only used to cook a string from.
- * `ev' is the translation of `OSInputMsg'.
- */
- VALUE
- usekey(void *OSInputMsg, Event *ev, bool cursState)
- {
- VALUE cmd, result = sym_nil;
- VW *vw = CurrVW;
- bool inmulti = !NILP(NextKeymapPath);
- CurrentEvent = ev;
- resettitle(vw);
- cmd = evalbinding(&ev->ev_EventDef);
- if(cmd)
- {
- if(cursState)
- {
- cursor(vw, CURS_OFF);
- cursState = FALSE;
- }
- switch(VTYPE(cmd))
- {
- case V_Symbol:
- result = funcall(cmd, sym_nil);
- break;
- case V_Cons:
- if(VCAR(cmd) == sym_lambda)
- {
- result = funcall(cmd, sym_nil);
- break;
- }
- /* FALL THROUGH */
- default:
- result = cmd_eval(cmd);
- }
- }
- else if(inmulti)
- beep(vw);
- else if(ev->ev_EventDef.evd_Type == EV_TYPE_KEYBD)
- {
- u_char buff[256];
- int len;
- if((len = cookkey(OSInputMsg, buff, 256 - 1)) > 0)
- {
- if(cursState)
- {
- cursor(vw, CURS_OFF);
- cursState = FALSE;
- }
- buff[len] = 0;
- if((!(result = cmd_eval_hook2(sym_unbound_key_hook, sym_nil)))
- || NILP(result))
- {
- if(!readonly(vw->vw_Tx) && padcursor(vw))
- {
- POS start, tmp;
- start = tmp = vw->vw_CursorPos;
- insertstrn(vw->vw_Tx, buff, len, &tmp);
- flaginsertion(vw->vw_Tx, &start, &vw->vw_CursorPos);
- result = sym_t;
- }
- else
- result = NULL;
- }
- }
- else if(len < 0)
- settitle("error: key translation screwup");
- }
- CurrentEvent = NULL;
- if(CurrVW)
- {
- refreshworld();
- if(!cursState)
- cursor(CurrVW, CURS_ON);
- }
- return(result);
- }
-
- void
- keymap_sweep(void)
- {
- Keytab *kt = KeytabChain;
- Keylist *kl = KeylistChain;
- KeytabChain = NULL;
- KeylistChain = NULL;
- while(kt)
- {
- Keytab *nxtkt = kt->kt_Next;
- if(!GC_MARKEDP(kt))
- {
- int i;
- for(i = 0; i < 128; i++)
- {
- Key *this = kt->kt_Keys[i];
- while(this)
- {
- Key *nxt = this->ky_Link.next;
- mystrfree(this);
- this = nxt;
- }
- }
- myfree(kt);
- }
- else
- {
- GC_CLR(kt);
- kt->kt_Next = KeytabChain;
- KeytabChain = kt;
- }
- kt = nxtkt;
- }
- while(kl)
- {
- Keylist *nxtkl = kl->kl_Next;
- if(!GC_MARKEDP(kl))
- {
- Key *this, *next;
- this = (Key *)kl->kl_List.mlh_Head;
- while((next = (Key *)this->ky_Link.node.mln_Succ))
- {
- mystrfree(this);
- this = next;
- }
- mystrfree(kl);
- }
- else
- {
- GC_CLR(kl);
- kl->kl_Next = KeylistChain;
- KeylistChain = kl;
- }
- kl = nxtkl;
- }
- }
- void
- keymap_prin(VALUE strm, VALUE obj)
- {
- switch(VTYPE(obj))
- {
- case V_Keytab:
- streamputs(strm, "#<key-table>", FALSE);
- break;
- case V_Keylist:
- streamputs(strm, "#<key-list>", FALSE);
- break;
- }
- }
-
- _PR VALUE cmd_make_keytab(void);
- DEFUN("make-keytab", cmd_make_keytab, subr_make_keytab, (void), V_Subr0, DOC_make_keytab) /*
- ::doc:make_keytab::
- (make-keytab)
- Return a new key-table suitable for storing bindings in.
- ::end:: */
- {
- Keytab *kt = mycalloc(sizeof(Keytab));
- if(kt)
- {
- kt->kt_Type = V_Keytab;
- kt->kt_Next = KeytabChain;
- KeytabChain = kt;
- }
- return(kt);
- }
-
- _PR VALUE cmd_make_keylist(void);
- DEFUN("make-keylist", cmd_make_keylist, subr_make_keylist, (void), V_Subr0, DOC_make_keylist) /*
- ::doc:make_keylist::
- (make-keylist)
- Return a new key-list suitable for storing bindings in.
- ::end:: */
- {
- Keylist *kl = mystralloc(sizeof(Keylist));
- if(kl)
- {
- kl->kl_Type = V_Keylist;
- kl->kl_Next = KeylistChain;
- KeylistChain = kl;
- NewMList(&kl->kl_List);
- }
- return(kl);
- }
-
- _PR VALUE cmd_bind_keys(VALUE args);
- DEFUN("bind-keys", cmd_bind_keys, subr_bind_keys, (VALUE args), V_SubrN, DOC_bind_keys) /*
- ::doc:bind_keys::
- (bind-keys KEY-MAP { KEY-DESCRIPTION FUNCTION }...)
- ::end:: */
- {
- bool rc = TRUE;
- bool iskt;
- VALUE km, arg1, res = NULL;
- if(!CONSP(args))
- return(NULL);
- km = VCAR(args);
- switch(VTYPE(km))
- {
- case V_Keytab:
- iskt = TRUE;
- break;
- case V_Keylist:
- iskt = FALSE;
- break;
- default:
- goto end;
- }
- args = VCDR(args);
- while(rc && CONSP(args) && CONSP(VCDR(args)))
- {
- EventDef ie;
- Key *rt;
- arg1 = VCAR(args);
- args = VCDR(args);
- ie.evd_Type = ie.evd_Mods = ie.evd_Code = 0;
- if(STRINGP(arg1))
- {
- if(!lookupevent(&ie, VSTR(arg1)))
- goto end;
- }
- else
- {
- cmd_signal(sym_bad_event_desc, LIST_1(arg1));
- goto end;
- }
- rc = FALSE;
- rt = mystralloc(sizeof(Key));
- if(rt)
- {
- if(iskt)
- {
- u_long entry = ie.evd_Code & EV_HASH_MASK;
- rt->ky_Link.next = VKEYTAB(km)->kt_Keys[entry];
- VKEYTAB(km)->kt_Keys[entry] = rt;
- }
- else
- InsertM(&VKEYLIST(km)->kl_List, &rt->ky_Link.node, NULL);
- rt->ky_Form = VCAR(args);
- args = VCDR(args);
- rt->ky_Event = ie;
- rc = TRUE;
- }
- else
- goto end;
- }
- if(rc)
- res = (sym_t);
- end:
- return(res);
- }
-
- _PR VALUE cmd_unbind_keys(VALUE args);
- DEFUN("unbind-keys", cmd_unbind_keys, subr_unbind_keys, (VALUE args), V_SubrN, DOC_unbind_keys) /*
- ::doc:unbind_keys::
- (unbind-keys KEY-MAP KEY-DESCRIPTION...)
- ::end:: */
- {
- bool rc = TRUE;
- bool iskt;
- VALUE km, arg1, res = NULL;
- if(!CONSP(args))
- return(NULL);
- km = VCAR(args);
- args = VCDR(args);
- switch(VTYPE(km))
- {
- case V_Keytab:
- iskt = TRUE;
- break;
- case V_Keylist:
- iskt = FALSE;
- break;
- default:
- goto end;
- }
- while(rc && CONSP(args))
- {
- EventDef ie;
- Key *tmp;
- ie.evd_Type = ie.evd_Mods = ie.evd_Code = 0;
- arg1 = VCAR(args);
- if(STRINGP(arg1))
- {
- if(!lookupevent(&ie, VSTR(arg1)))
- goto end;
- }
- else
- {
- cmd_signal(sym_bad_event_desc, LIST_1(arg1));
- goto end;
- }
- rc = FALSE;
- tmp = findkey(km, &ie);
- if(tmp)
- {
- if(iskt)
- {
- u_long code = ie.evd_Code & EV_HASH_MASK;
- if(VKEYTAB(km)->kt_Keys[code] == tmp)
- VKEYTAB(km)->kt_Keys[code] = tmp->ky_Link.next;
- else
- {
- Key **last = &(VKEYTAB(km)->kt_Keys[code]);
- while((*last)->ky_Link.next != tmp)
- last = &((*last)->ky_Link.next);
- (*last)->ky_Link.next = tmp->ky_Link.next;
- }
- }
- else
- RemoveM(&tmp->ky_Link.node);
- mystrfree(tmp);
- rc = TRUE;
- }
- args = VCDR(args);
- }
- if(rc)
- res = sym_t;
- end:
- return(res);
- }
-
- _PR VALUE var_next_keymap_path(VALUE val);
- DEFUN("next-keymap-path", var_next_keymap_path, subr_next_keymap_path, (VALUE val), V_Var, DOC_next_keymap_path) /*
- ::doc:next_keymap_path::
- The value of `keymap-path' to be used for the *next* keypress. This is
- usually used to chain together multi-key bindings.
- ::end:: */
- {
- if(val)
- NextKeymapPath = val;
- return(NextKeymapPath);
- }
-
- void
- keys_init(void)
- {
- INTERN(sym_keymap_path, "keymap-path");
- DOC_VAR(sym_keymap_path, DOC_keymap_path);
- INTERN(sym_unbound_key_hook, "unbound-key-hook");
- DOC_VAR(sym_unbound_key_hook, DOC_unbound_key_hook);
- NextKeymapPath = sym_nil;
- markstatic(&NextKeymapPath);
- ADD_SUBR(subr_make_keytab);
- ADD_SUBR(subr_make_keylist);
- ADD_SUBR(subr_bind_keys);
- ADD_SUBR(subr_unbind_keys);
- ADD_SUBR(subr_next_keymap_path);
- }
- void
- keys_kill(void)
- {
- Keytab *kt = KeytabChain;
- Keylist *kl = KeylistChain;
- while(kt)
- {
- int i;
- Keytab *nxtkt = kt->kt_Next;
- for(i = 0; i < 128; i++)
- {
- Key *this = kt->kt_Keys[i];
- while(this)
- {
- Key *nxt = this->ky_Link.next;
- mystrfree(this);
- this = nxt;
- }
- }
- myfree(kt);
- kt = nxtkt;
- }
- while(kl)
- {
- Key *this, *next;
- Keylist *nxtkl = kl->kl_Next;
- this = (Key *)kl->kl_List.mlh_Head;
- while((next = (Key *)this->ky_Link.node.mln_Succ))
- {
- mystrfree(this);
- this = next;
- }
- mystrfree(kl);
- kl = nxtkl;
- }
- }
-