Browse Source

Added customisable keymapping to zenith

Keith Poole 7 years ago
parent
commit
52464f5a98

+ 9 - 2
sys/src/cmd/zenith/acme.c

@@ -68,10 +68,10 @@ threadmain(int argc, char *argv[])
 	int i;
 	char *p, *loadfile;
 	char buf[256];
-	Column *c;
+	char* initscript  = "zenith.init"
+;	Column *c;
 	int ncol;
 	Display *d;
-//
 
 	rfork(RFENVG|RFNAMEG);
 
@@ -108,6 +108,11 @@ threadmain(int argc, char *argv[])
 		if(loadfile == nil)
 			goto Usage;
 		break;
+	case 'i':
+		initscript = ARGF();
+		if(initscript == nil)
+			goto Usage;
+		break;
 	default:
 	Usage:
 		fprint(2, "usage: acme [-ab] [-c ncol] [-f font] [-F fixedfont] [-l loadfile | file...]\n");
@@ -234,6 +239,8 @@ threadmain(int argc, char *argv[])
 	threadcreate(waitthread, nil, STACK);
 	threadcreate(xfidallocthread, nil, STACK);
 	threadcreate(newwindowthread, nil, STACK);
+	loadinitscript("/bin/zenith.init");
+	loadinitscript(initscript);
 
 	threadnotify(shutdown, 1);
 	recvul(cexit);

+ 2 - 1
sys/src/cmd/zenith/build.json

@@ -30,7 +30,8 @@
 			"xfid.c"
 		],
 	        "Post": [
-	                "mkdir -p $HARVEY/mnt/acme"
+	                "mkdir -p $HARVEY/mnt/acme",
+			"cp zenith.init $HARVEY/$ARCH/bin"
 	        ]
 	}
 }

+ 1 - 1
sys/src/cmd/zenith/cols.c

@@ -40,7 +40,7 @@ colinit(Column *c, Rectangle r)
 	r1.min.y = r1.max.y;
 	r1.max.y += Border;
 	draw(screen, r1, display->black, nil, ZP);
-	textinsert(t, 0, L"New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE);
+	textinsert(t, 0, (Rune*)L"New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE);
 	textsetselect(t, t->file->Buffer.nc, t->file->Buffer.nc);
 	draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
 	c->safe = TRUE;

+ 2 - 0
sys/src/cmd/zenith/dat.h

@@ -13,6 +13,7 @@ enum
 	Qacme,
 	Qcons,
 	Qconsctl,
+	Qctl,
 	Qdraw,
 	Qeditout,
 	Qindex,
@@ -200,6 +201,7 @@ struct Text
 	int		ncachealloc;
 	Rune	*cache;
 	int	nofill;
+	int extended;
 };
 
 unsigned int		textbacknl(Text*, unsigned int, unsigned int);

+ 1 - 1
sys/src/cmd/zenith/ecmd.c

@@ -362,7 +362,7 @@ f_cmd(Text *t, Cmd *cp)
 
 	if(cp->text == nil){
 		empty.n = 0;
-		empty.r = L"";
+		empty.r = (Rune*)L"";
 		str = ∅
 	}else
 		str = cp->text;

+ 45 - 45
sys/src/cmd/zenith/exec.c

@@ -71,34 +71,34 @@ struct Exectab
 };
 
 Exectab exectab[] = {
-	{ L"Cut",		cut,		TRUE,	TRUE,	TRUE	},
-	{ L"Del",		del,		FALSE,	FALSE,	XXX		},
-	{ L"Delcol",	delcol,	FALSE,	XXX,		XXX		},
-	{ L"Delete",	del,		FALSE,	TRUE,	XXX		},
-	{ L"Dump",	dump,	FALSE,	TRUE,	XXX		},
-	{ L"Edit",		edit,		FALSE,	XXX,		XXX		},
-	{ L"Exit",		exit,		FALSE,	XXX,		XXX		},
-	{ L"Font",		fontx,	FALSE,	XXX,		XXX		},
-	{ L"Get",		get,		FALSE,	TRUE,	XXX		},
-	{ L"ID",		id,		FALSE,	XXX,		XXX		},
-	{ L"Incl",		incl,		FALSE,	XXX,		XXX		},
-	{ L"Indent",	indent,	FALSE,	XXX,		XXX		},
-	{ L"Kill",		kill,		FALSE,	XXX,		XXX		},
-	{ L"Load",		dump,	FALSE,	FALSE,	XXX		},
-	{ L"Local",		local,	FALSE,	XXX,		XXX		},
-	{ L"Look",		look,		FALSE,	XXX,		XXX		},
-	{ L"New",		new,		FALSE,	XXX,		XXX		},
-	{ L"Newcol",	newcol,	FALSE,	XXX,		XXX		},
-	{ L"Paste",		paste,	TRUE,	TRUE,	XXX		},
-	{ L"Put",		put,		FALSE,	XXX,		XXX		},
-	{ L"Putall",		putall,	FALSE,	XXX,		XXX		},
-	{ L"Redo",		undo,	FALSE,	FALSE,	XXX		},
-	{ L"Send",		sendx,	TRUE,	XXX,		XXX		},
-	{ L"Snarf",		cut,		FALSE,	TRUE,	FALSE	},
-	{ L"Sort",		sort,		FALSE,	XXX,		XXX		},
-	{ L"Tab",		tab,		FALSE,	XXX,		XXX		},
-	{ L"Undo",		undo,	FALSE,	TRUE,	XXX		},
-	{ L"Zerox",	zeroxx,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Cut",		cut,		TRUE,	TRUE,	TRUE	},
+	{ (Rune *)L"Del",		del,		FALSE,	FALSE,	XXX		},
+	{ (Rune *)L"Delcol",	delcol,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Delete",	del,		FALSE,	TRUE,	XXX		},
+	{ (Rune *)L"Dump",	dump,	FALSE,	TRUE,	XXX		},
+	{ (Rune *)L"Edit",		edit,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Exit",		exit,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Font",		fontx,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Get",		get,		FALSE,	TRUE,	XXX		},
+	{ (Rune *)L"ID",		id,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Incl",		incl,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Indent",	indent,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Kill",		kill,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Load",		dump,	FALSE,	FALSE,	XXX		},
+	{ (Rune *)L"Local",		local,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Look",		look,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"New",		new,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Newcol",	newcol,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Paste",		paste,	TRUE,	TRUE,	XXX		},
+	{ (Rune *)L"Put",		put,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Putall",		putall,	FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Redo",		undo,	FALSE,	FALSE,	XXX		},
+	{ (Rune *)L"Send",		sendx,	TRUE,	XXX,		XXX		},
+	{ (Rune *)L"Snarf",		cut,		FALSE,	TRUE,	FALSE	},
+	{ (Rune *)L"Sort",		sort,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Tab",		tab,		FALSE,	XXX,		XXX		},
+	{ (Rune *)L"Undo",		undo,	FALSE,	TRUE,	XXX		},
+	{ (Rune *)L"Zerox",	zeroxx,	FALSE,	XXX,		XXX		},
 	{ nil, 			nil,		0,		0,		0		},
 };
 
@@ -873,7 +873,7 @@ sendx(Text *et, Text *t, Text*a, int b, int c, Rune*d, int f)
 	textsetselect(t, t->file->Buffer.nc, t->file->Buffer.nc);
 	paste(t, t, nil, TRUE, TRUE, nil, 0);
 	if(textreadc(t, t->file->Buffer.nc-1) != '\n'){
-		textinsert(t, t->file->Buffer.nc, L"\n", 1, TRUE);
+		textinsert(t, t->file->Buffer.nc, (Rune *)L"\n", 1, TRUE);
 		textsetselect(t, t->file->Buffer.nc, t->file->Buffer.nc);
 	}
 }
@@ -1004,7 +1004,7 @@ fontx(Text *et, Text *t, Text *argt, int ia, int b, Rune *arg, int narg)
 			break;
 		r = runemalloc(narg-na+1);
 		runemove(r, arg, narg-na);
-		if(runeeq(r, narg-na, L"fix", 3) || runeeq(r, narg-na, L"var", 3)){
+		if(runeeq(r, narg-na, (Rune *)L"fix", 3) || runeeq(r, narg-na, (Rune *)L"var", 3)){
 			free(flag);
 			flag = r;
 		}else{
@@ -1016,7 +1016,7 @@ fontx(Text *et, Text *t, Text *argt, int ia, int b, Rune *arg, int narg)
 	}
 	getarg(argt, FALSE, TRUE, &r, &na);
 	if(r)
-		if(runeeq(r, na, L"fix", 3) || runeeq(r, na, L"var", 3)){
+		if(runeeq(r, na, (Rune *)L"fix", 3) || runeeq(r, na, (Rune *)L"var", 3)){
 			free(flag);
 			flag = r;
 		}else{
@@ -1026,7 +1026,7 @@ fontx(Text *et, Text *t, Text *argt, int ia, int b, Rune *arg, int narg)
 		}
 	fix = 1;
 	if(flag)
-		fix = runeeq(flag, runestrlen(flag), L"fix", 3);
+		fix = runeeq(flag, runestrlen(flag), (Rune *)L"fix", 3);
 	else if(file == nil){
 		newfont = rfget(FALSE, FALSE, FALSE, nil);
 		if(newfont)
@@ -1106,17 +1106,17 @@ indentval(Rune *s, int n)
 {
 	if(n < 2)
 		return IError;
-	if(runestrncmp(s, L"ON", n) == 0){
+	if(runestrncmp(s, (Rune *)L"ON", n) == 0){
 		globalautoindent = TRUE;
 		warning(nil, "Indent ON\n");
 		return IGlobal;
 	}
-	if(runestrncmp(s, L"OFF", n) == 0){
+	if(runestrncmp(s, (Rune *)L"OFF", n) == 0){
 		globalautoindent = FALSE;
 		warning(nil, "Indent OFF\n");
 		return IGlobal;
 	}
-	return runestrncmp(s, L"on", n) == 0;
+	return runestrncmp(s, (Rune *)L"on", n) == 0;
 }
 
 static void
@@ -1190,16 +1190,16 @@ void
 runproc(void *argvp)
 {
 	/* args: */
-		Window *win;
-		char *s;
-		Rune *rdir;
-		int ndir;
-		int newns;
-		char *argaddr;
-		char *arg;
-		Command *c;
-		Channel *cpid;
-		int iseditcmd;
+	Window *win;
+	char *s;
+	Rune *rdir;
+	int ndir;
+	int newns;
+	char *argaddr;
+	char *arg;
+	Command *c;
+	Channel *cpid;
+	int iseditcmd;
 	/* end of args */
 	char *e, *t, *name, *filename, *dir, **av, *news;
 	Rune r, **incl;

+ 4 - 1
sys/src/cmd/zenith/fns.h

@@ -74,7 +74,7 @@ Window*	lookfile(Rune*, int);
 Window*	lookid(int, int);
 char*	runetobyte(Rune*, int);
 Rune*	bytetorune(char*, int*);
-void	fsysinit(void);
+void	fsysinit();
 Mntdir*	fsysmount(Rune*, int, Rune**, int);
 void		fsysincid(Mntdir*);
 void		fsysdelid(Mntdir*);
@@ -96,6 +96,9 @@ Rune*	skipbl(Rune*, int, int*);
 Rune*	findbl(Rune*, int, int*);
 char*	edittext(Window*, int, Rune*, int);
 void		flushwarnings(void);
+void	loadinitscript(const char* initscript);
+int	keymap(char* key, char* mapping);
+int	extendedkeymap(char* key, char* mapping);
 
 #define	runemalloc(a)		(Rune*)emalloc((a)*sizeof(Rune))
 #define	runerealloc(a, b)	(Rune*)erealloc((a), (b)*sizeof(Rune))

+ 5 - 4
sys/src/cmd/zenith/fsys.c

@@ -73,9 +73,10 @@ Dirtab dirtab[]=
 	{ ".",			QTDIR,	Qdir,		0500|DMDIR },
 	{ "acme",		QTDIR,	Qacme,	0500|DMDIR },
 	{ "cons",		QTFILE,	Qcons,	0600 },
-	{ "consctl",	QTFILE,	Qconsctl,	0000 },
+	{ "consctl",		QTFILE,	Qconsctl,	0000 },
+	{ "ctl",		QTFILE,	Qctl,	0600 },
 	{ "draw",		QTDIR,	Qdraw,	0000|DMDIR },	/* to suppress graphics progs started in acme */
-	{ "editout",	QTFILE,	Qeditout,	0200 },
+	{ "editout",		QTFILE,	Qeditout,	0200 },
 	{ "index",		QTFILE,	Qindex,	0400 },
 	{ "label",		QTFILE,	Qlabel,	0600 },
 	{ "new",		QTDIR,	Qnew,	0500|DMDIR },
@@ -89,7 +90,7 @@ Dirtab dirtabw[]=
 	{ "body",		QTAPPEND,	QWbody,		0600|DMAPPEND },
 	{ "ctl",		QTFILE,		QWctl,		0600 },
 	{ "data",		QTFILE,		QWdata,		0600 },
-	{ "editout",	QTFILE,		QWeditout,	0200 },
+	{ "editout",		QTFILE,		QWeditout,	0200 },
 	{ "errors",		QTFILE,		QWerrors,		0200 },
 	{ "event",		QTFILE,		QWevent,		0600 },
 	{ "rdsel",		QTFILE,		QWrdsel,		0400 },
@@ -121,7 +122,7 @@ int	messagesize = Maxblock+IOHDRSZ;	/* good start */
 void	fsysproc(void *);
 
 void
-fsysinit(void)
+fsysinit()
 {
 	int p[2];
 	int n, fd;

+ 2 - 2
sys/src/cmd/zenith/look.c

@@ -323,7 +323,7 @@ isfilec(Rune r)
 {
 	if(isalnum(r))
 		return TRUE;
-	if(runestrchr(L".-+/:", r))
+	if(runestrchr((Rune *)L".-+/:", r))
 		return TRUE;
 	return FALSE;
 }
@@ -392,7 +392,7 @@ includename(Text *t, Rune *r, int n)
 		file = includefile(w->incl[i], r, n);
 
 	if(file.r == nil)
-		file = includefile(L"/sys/include", r, n);
+		file = includefile((Rune *)L"/sys/include", r, n);
 	if(file.r==nil && objdir!=nil)
 		file = includefile(objdir, r, n);
 	if(file.r == nil)

+ 1 - 1
sys/src/cmd/zenith/rows.c

@@ -42,7 +42,7 @@ rowinit(Row *row, Rectangle r)
 	r1.min.y = r1.max.y;
 	r1.max.y += Border;
 	draw(screen, r1, display->black, nil, ZP);
-	textinsert(t, 0, L"Newcol Kill Putall Dump Exit ", 29, TRUE);
+	textinsert(t, 0, (Rune *)L"Newcol Kill Putall Dump Exit ", 29, TRUE);
 	textsetselect(t, t->file->Buffer.nc, t->file->Buffer.nc);
 }
 

+ 546 - 176
sys/src/cmd/zenith/text.c

@@ -28,6 +28,193 @@ enum{
 	TABDIR = 3	/* width of tabs in directory windows */
 };
 
+typedef void (*keyhandler)(Text *t, Rune r);
+
+struct Builtin {
+    char* name;
+    keyhandler handler;
+};
+
+struct Mapping {
+    struct Mapping* next;
+    int extended;
+    int key;
+    keyhandler handler;
+    char* external;
+};
+
+void worddeletehandler(Text *t, Rune r);
+void deletetostartoflinehandler(Text *t, Rune r);
+void backspacehandler(Text *t, Rune r);
+void pguphandler(Text *t, Rune r);
+void pgdownhandler(Text *t, Rune r);
+void executelinehandler(Text *t, Rune r);
+void inshandler(Text *t, Rune r);
+void selecthandler(Text *t, Rune r);
+void nlhandler(Text *t, Rune r);
+void beginline(Text *t, Rune r);
+void goend(Text *t, Rune r);
+void executeline(Text *t, Rune r);
+void goleft(Text *t, Rune r);
+void goright(Text *t, Rune r);
+void godown(Text *t, Rune r);
+void goup(Text *t, Rune r);
+void gohome(Text *t, Rune r);
+void endline(Text *t, Rune r);
+void extendedhandler(Text *t, Rune r);
+
+void wheeluphandler(Text *t, Rune r);
+void wheeldownhandler(Text *t, Rune r);
+
+struct Builtin builtins[] = {
+    {"start_of_line", beginline},
+    {"delete_word_before_cursor", worddeletehandler},
+    {"delete_to_start_of_line", deletetostartoflinehandler},
+    {"backspace", backspacehandler},
+    {"end_of_line", endline},
+    {"execute_line", executeline},
+    {"left", goleft},
+    {"right", goright},
+    {"down", godown},
+    {"up", goup},
+    {"pgup", pguphandler},
+    {"pgdown", pgdownhandler},
+    {"home", gohome},
+    {"end", goend},
+    {"ins", inshandler},
+    {"select", selecthandler},
+    {"nl", nlhandler},
+    {"extend", extendedhandler},
+    {"wheelup", wheeluphandler},
+	{"wheeldown", wheeldownhandler}};
+
+struct Mapping* mappings;
+
+struct Keynames {
+    char* name;
+    int value;
+};
+
+struct Keynames keynames[] = {
+    {"F1", KF|1},
+    {"F2", KF|2},
+    {"F3", KF|3},
+    {"F4", KF|4},
+    {"F5", KF|5},
+    {"F6", KF|6},
+    {"F7", KF|7},
+    {"F8", KF|8},
+    {"F9", KF|9},
+ 	{"F10", KF|10},
+    {"F11", KF|11},
+    {"F12", KF|12},
+    {"home", Khome},
+    {"up", Kup},
+    {"pgup", Kpgup},
+    {"print", Kprint},
+    {"left", Kleft},
+    {"right", Kright},
+    {"down", Kdown},
+    {"view", Kview},
+    {"pgdown", Kpgdown},
+    {"ins", Kins},
+    {"end", Kend},
+    {"bs", Kbs},
+    {"del", Kdel},
+    {"esc", Kesc},
+	{"wheelup", Kscrolloneup},
+	{"wheeldown", Kscrollonedown},
+    {"nl", (int)'\n'}
+};
+
+int numkeys = sizeof(keynames) / sizeof(struct Keynames);
+
+struct Mapping*
+findmapping(int k, int extended)
+{
+    struct Mapping* m = mappings;
+    while (m != nil) {
+        if (k == m->key && extended == m->extended) {
+            return m;
+        }
+        m = m->next;
+    }
+    return nil;
+}
+
+struct Builtin *
+findhandler(char * name)
+{
+    int i;
+
+    for(i = 0; i < (sizeof(builtins)/sizeof(struct Builtin)); i++){
+        if(strcmp(name, builtins[i].name) == 0){
+            return &builtins[i];
+        }
+    }
+    return nil;
+}
+
+
+int
+stringtokey(char *key)
+{
+    int i;
+
+    if(strlen(key) == 1){
+        return (int)key[0] & 0x1f;
+    }
+    for(i=0; i<numkeys; i++){
+        if(cistrcmp(keynames[i].name, key) == 0){
+            return keynames[i].value;
+        }
+    }
+    return -1;
+}
+
+int
+addmapping(char* key, char* mappingname, int extended)
+{
+    int k;
+	if(key == nil || mappingname == nil){
+		return 1;
+	}
+	k = stringtokey(key);
+	if (k == -1){
+	    return 1;
+	}
+	struct Mapping* mapping = findmapping(k, extended);
+	if (mapping == nil){
+	    mapping = malloc(sizeof(struct Mapping));
+        mapping->next = mappings;
+        mappings = mapping;
+	}
+	mapping->key = k;
+	mapping->extended = extended;
+    struct Builtin *builtin = findhandler(mappingname);
+    if(builtin == nil){
+        mapping->external = malloc(strlen(mappingname));
+        strcpy(mapping->external, mappingname);
+        mapping->handler = nil;
+    } else {
+        mapping->external = nil;
+        mapping->handler = builtin->handler;
+    }
+    return 0;
+}
+
+int
+keymap(char* key, char* mapping)
+{
+	return addmapping(key, mapping, FALSE);
+}
+
+int
+extendedkeymap(char* key, char* mapping)
+{
+    return addmapping(key, mapping, TRUE);
+}
+
 void
 textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
 {
@@ -45,6 +232,43 @@ textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
 	textredraw(t, r, rf->f, screen, -1);
 }
 
+void
+runexternal(char* command, char* where)
+{
+    int wherelen;
+	Rune* whereRune;
+
+	wherelen = utflen(where)*sizeof(Rune);
+	whereRune = runemalloc(wherelen);
+	runesnprint(whereRune, wherelen, "%s", where);
+	run(nil, command, whereRune, runestrlen(whereRune), TRUE, nil, nil, FALSE);
+}
+
+void
+loadinitscript(const char* initscript)
+{
+	char *path;
+	char *home = getenv("home");
+	Dir  *initfile;
+
+	if(initscript[0] == '/'){
+		path = malloc(strlen(initscript)+1);
+		strcpy(path, initscript);
+	} else {
+		path = malloc(strlen(home) + strlen(initscript) + 6);
+		strcpy(path, home);
+		strcat(path, "/lib/");
+		strcat(path, initscript);
+	}
+	initfile = dirstat(path);
+	if (initfile != nil){
+		runexternal(path, home);
+	}else{
+		free(path);
+	}
+	free(initfile);
+}
+
 void
 textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
 {
@@ -171,17 +395,17 @@ textcolumnate(Text *t, Dirlist **dlp, int ndl)
 				break;
 			w = dl->wid;
 			if(maxt-w%maxt < mint){
-				fileinsert(t->file, q1, L"\t", 1);
+				fileinsert(t->file, q1, (Rune *)L"\t", 1);
 				q1++;
 				w += mint;
 			}
 			do{
-				fileinsert(t->file, q1, L"\t", 1);
+				fileinsert(t->file, q1, (Rune *)L"\t", 1);
 				q1++;
 				w += maxt-(w%maxt);
 			}while(w < colw);
 		}
-		fileinsert(t->file, q1, L"\n", 1);
+		fileinsert(t->file, q1, (Rune *)L"\n", 1);
 		q1++;
 	}
 }
@@ -601,7 +825,7 @@ textcomplete(Text *t)
 		}
 		if(dir.nr == 0){
 			dir.nr = 1;
-			dir.r = runestrdup(L".");
+			dir.r = runestrdup((Rune *)L".");
 		}
 		runemove(tmp, dir.r, dir.nr);
 		tmp[dir.nr] = '/';
@@ -644,105 +868,45 @@ textcomplete(Text *t)
 }
 
 void
-texttype(Text *t, Rune r)
+goleft(Text *t, Rune r)
 {
-	uint q0, q1;
-	int nnb, nb, n, i;
-	int nr;
-	int linelen;
-	Rune *rp;
-	Text *u;
-
-	if(t->what!=Body && r=='\n')
-		return;
-	nr = 1;
-	rp = &r;
-	switch(r){
-	case Kleft:
-	case_Back:
-		if(t->q0 > 0){
-			typecommit(t);
-			textshow(t, t->q0-1, t->q0-1, TRUE);
-		}
-		return;
-	case Kright:
-		if(t->q1 < t->file->Buffer.nc){
-			typecommit(t);
-			textshow(t, t->q1+1, t->q1+1, TRUE);
-		}
-		return;
-	case Kdown:
-	        typecommit(t);
-	        q0 = t->q0;
-	        nnb = textbswidth(t, 0x15) + 1;
-	        while(q0<t->file->Buffer.nc && textreadc(t, q0)!='\n')
-			q0++;
-		if (q0 + nnb > t->file->Buffer.nc){
-			q0 = t->file->Buffer.nc;
-		} else {
-			q0 = q0 + nnb;
-		}
-	        textshow(t, q0, q0, TRUE);
-	        return;
-	case Kscrollonedown:
-		n = mousescrollsize(t->Frame.maxlines);
-		if(n <= 0)
-			n = 1;
-		goto case_Down;
-	case Kpgdown:
-		n = 2*t->Frame.maxlines/3;
-	case_Down:
-		q0 = t->org+frcharofpt(&t->Frame, Pt(t->Frame.r.min.x, t->Frame.r.min.y+n*t->Frame.font->height));
-		textsetorigin(t, q0, TRUE);
-		return;
-	case Kup:
-		typecommit(t);
-		nnb = 0;
-		if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
-			nnb = textbswidth(t, 0x15); 
-		if( t->q0-nnb > 1  && textreadc(t, t->q0-nnb-1)=='\n' ) nnb++; 
-		textshow(t, t->q0-nnb, t->q0-nnb, TRUE); 
-		linelen = textbswidth(t, 0x15);
-		if(t->q0 - (linelen - nnb) <= 0){
-			goto case_Back;
-		}
-		if (linelen > nnb){
-			textshow(t, t->q0 - (linelen - nnb)-1, t->q0 - (linelen - nnb)-1, TRUE);
-		}
-		return; 
-	case Kscrolloneup:
-		n = mousescrollsize(t->Frame.maxlines);
-		goto case_Up;
-	case Kpgup:
-		n = 2*t->Frame.maxlines/3;
-	case_Up:
-		q0 = textbacknl(t, t->org, n);
-		textsetorigin(t, q0, TRUE);
-		return;
-	case Khome:
-		typecommit(t);
-		textshow(t, 0, 0, FALSE);
-		return;
-	case Kend:
-		typecommit(t);
-		textshow(t, t->file->Buffer.nc, t->file->Buffer.nc, FALSE);
-		return;
-	case 0x01:	/* ^A: beginning of line */
+	if(t->q0 > 0){
 		typecommit(t);
-		/* go to where ^U would erase, if not already at BOL */
-		nnb = 0;
-		if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
-			nnb = textbswidth(t, 0x15);
-		textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
-		return;
-	case 0x05:	/* ^E: end of line */
-		typecommit(t);
-		q0 = t->q0;
-		while(q0<t->file->Buffer.nc && textreadc(t, q0)!='\n')
-			q0++;
-		textshow(t, q0, q0, TRUE);
-		return;
-	}
+                textshow(t, t->q0-1, t->q0-1, TRUE);
+        }
+}
+
+void
+goright(Text* t, Rune r)
+{
+    if(t->q1 < t->file->Buffer.nc){
+        typecommit(t);
+        textshow(t, t->q1+1, t->q1+1, TRUE);
+    }
+}
+
+void
+godown(Text* t, Rune r)
+{
+    uint q0;
+    int nnb;
+
+    typecommit(t);
+    q0 = t->q0;
+    nnb = textbswidth(t, 0x15) + 1;
+    while(q0<t->file->Buffer.nc && textreadc(t, q0)!='\n')
+        q0++;
+    if (q0 + nnb > t->file->Buffer.nc){
+        q0 = t->file->Buffer.nc;
+    } else {
+        q0 = q0 + nnb;
+    }
+    textshow(t, q0, q0, TRUE);
+}
+
+void
+updatebody(Text* t)
+{
 	if(t->what == Body){
 		seq++;
 		filemark(t->file);
@@ -754,79 +918,14 @@ texttype(Text *t, Rune r)
 		t->eq0 = ~0;
 	}
 	textshow(t, t->q0, t->q0, 1);
-	switch(r){
-	case 0x06:
-	case Kins:
-		rp = textcomplete(t);
-		if(rp == nil)
-			return;
-		nr = runestrlen(rp);
-		break;	/* fall through to normal insertion case */
-	case 0x1B:
-		if(t->eq0 != ~0)
-			textsetselect(t, t->eq0, t->q0);
-		if(t->ncache > 0)
-			typecommit(t);
-		return;
-	case 0x08:	/* ^H: erase character */
-	case 0x15:	/* ^U: erase line */
-	case 0x17:	/* ^W: erase word */
-		if(t->q0 == 0)	/* nothing to erase */
-			return;
-		nnb = textbswidth(t, r);
-		q1 = t->q0;
-		q0 = q1-nnb;
-		/* if selection is at beginning of window, avoid deleting invisible text */
-		if(q0 < t->org){
-			q0 = t->org;
-			nnb = q1-q0;
-		}
-		if(nnb <= 0)
-			return;
-		for(i=0; i<t->file->ntext; i++){
-			u = t->file->text[i];
-			u->nofill = TRUE;
-			nb = nnb;
-			n = u->ncache;
-			if(n > 0){
-				if(q1 != u->cq0+n)
-					error("text.type backspace");
-				if(n > nb)
-					n = nb;
-				u->ncache -= n;
-				textdelete(u, q1-n, q1, FALSE);
-				nb -= n;
-			}
-			if(u->eq0==q1 || u->eq0==~0)
-				u->eq0 = q0;
-			if(nb && u==t)
-				textdelete(u, q0, q0+nb, TRUE);
-			if(u != t)
-				textsetselect(u, u->q0, u->q1);
-			else
-				textsetselect(t, q0, q0);
-			u->nofill = FALSE;
-		}
-		for(i=0; i<t->file->ntext; i++)
-			textfill(t->file->text[i]);
-		return;
-	case '\n':
-		if(t->w->autoindent){
-			/* find beginning of previous line using backspace code */
-			nnb = textbswidth(t, 0x15); /* ^U case */
-			rp = runemalloc(nnb + 1);
-			nr = 0;
-			rp[nr++] = r;
-			for(i=0; i<nnb; i++){
-				r = textreadc(t, t->q0-nnb+i);
-				if(r != ' ' && r != '\t')
-					break;
-				rp[nr++] = r;
-			}
-		}
-		break; /* fall through to normal code */
-	}
-	/* otherwise ordinary character; just insert, typically in caches of all texts */
+}
+
+void
+insertcharacter(Text *t, int nr, Rune *rp, Rune r)
+{
+    Text *u;
+    int i;
+
 	for(i=0; i<t->file->ntext; i++){
 		u = t->file->text[i];
 		if(u->eq0 == ~0)
@@ -845,13 +944,284 @@ texttype(Text *t, Rune r)
 		runemove(u->cache+u->ncache, rp, nr);
 		u->ncache += nr;
 	}
-	if(rp != &r)
-		free(rp);
 	textsetselect(t, t->q0+nr, t->q0+nr);
 	if(r=='\n' && t->w!=nil)
 		wincommit(t->w, t);
 }
 
+void
+erase(Text * t, Rune r)
+{
+    int n, nb, i;
+    Text *u;
+
+    updatebody(t);
+    if(t->q0 == 0)	/* nothing to erase */
+        return;
+    int nnb = textbswidth(t, r);
+    uint q1 = t->q0;
+    uint q0 = q1-nnb;
+    /* if selection is at beginning of window, avoid deleting invisible text */
+    if(q0 < t->org){
+        q0 = t->org;
+        nnb = q1-q0;
+    }
+    if(nnb <= 0)
+        return;
+    for(i=0; i<t->file->ntext; i++){
+        u = t->file->text[i];
+        u->nofill = TRUE;
+        nb = nnb;
+        n = u->ncache;
+        if(n > 0){
+            if(q1 != u->cq0+n)
+                error("text.type backspace");
+            if(n > nb)
+                n = nb;
+            u->ncache -= n;
+            textdelete(u, q1-n, q1, FALSE);
+            nb -= n;
+        }
+        if(u->eq0==q1 || u->eq0==~0)
+            u->eq0 = q0;
+        if(nb && u==t)
+            textdelete(u, q0, q0+nb, TRUE);
+        if(u != t)
+            textsetselect(u, u->q0, u->q1);
+        else
+            textsetselect(t, q0, q0);
+        u->nofill = FALSE;
+    }
+    for(i=0; i<t->file->ntext; i++)
+        textfill(t->file->text[i]);
+}
+
+void
+scrolldown(Text* t, int n)
+{
+    uint q0 = t->org+frcharofpt(&t->Frame, Pt(t->Frame.r.min.x, t->Frame.r.min.y+n*t->Frame.font->height));
+    textsetorigin(t, q0, TRUE);
+}
+
+void
+scrollonedown(Text *t, Rune r)
+{
+    int n = mousescrollsize(t->Frame.maxlines);
+	if(n <= 0)
+		n = 1;
+	scrolldown(t, n);
+}
+
+void
+goup(Text* t, Rune r)
+{
+    int nnb;
+
+    typecommit(t);
+    nnb = 0;
+    if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
+        nnb = textbswidth(t, 0x15);
+    if( t->q0-nnb > 1  && textreadc(t, t->q0-nnb-1)=='\n' ) nnb++;
+    textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
+    int linelen = textbswidth(t, 0x15);
+    if(t->q0 - (linelen - nnb) <= 0){
+        goleft(t, r);
+        return;
+    }
+    if (linelen > nnb){
+        textshow(t, t->q0 - (linelen - nnb)-1, t->q0 - (linelen - nnb)-1, TRUE);
+    }
+}
+
+void
+caseup(Text* t, int n)
+{
+    uint q0 = textbacknl(t, t->org, n);
+    textsetorigin(t, q0, TRUE);
+}
+
+void
+gohome(Text* t, Rune r)
+{
+    typecommit(t);
+    textshow(t, 0, 0, FALSE);
+}
+
+void
+goend(Text *t, Rune r)
+{
+    typecommit(t);
+    textshow(t, t->file->Buffer.nc, t->file->Buffer.nc, FALSE);
+}
+
+void
+beginline(Text* t, Rune r)
+{
+    int nnb;
+
+    typecommit(t);
+    /* go to where ^U would erase, if not already at BOL */
+    nnb = 0;
+    if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
+        nnb = textbswidth(t, 0x15);
+    textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
+}
+
+void
+endline(Text *t, Rune r)
+{
+    uint q0;
+
+    typecommit(t);
+    q0 = t->q0;
+    while(q0<t->file->Buffer.nc && textreadc(t, q0)!='\n')
+        q0++;
+    textshow(t, q0, q0, TRUE);
+}
+
+void
+executeline(Text *t, Rune r)
+{
+    uint q0, q1;
+    typecommit(t);
+    q0 = t->q0 - textbswidth(t, 0x15);
+    q1 = t->q0;
+    while(q1<t->file->Buffer.nc && textreadc(t, q1)!='\n')
+            q1++;
+    execute(t, q0, q1, TRUE, nil);
+}
+
+void
+worddeletehandler(Text *t, Rune r)
+{
+    erase(t, 0x17);
+}
+
+void
+deletetostartoflinehandler(Text *t, Rune r)
+{
+    erase(t, 0x15);
+}
+
+void
+backspacehandler(Text *t, Rune r)
+{
+    erase(t, 0x8);
+}
+
+void
+pguphandler(Text *t, Rune r)
+{
+    caseup(t, 2*t->Frame.maxlines/3);
+}
+
+void
+pgdownhandler(Text *t, Rune r)
+{
+    scrolldown(t, 2*t->Frame.maxlines/3);
+}
+
+void
+inshandler(Text *t, Rune r)
+{
+    Rune* rp;
+    int nr;
+
+    updatebody(t);
+    rp = textcomplete(t);
+    if(rp == nil)
+        return;
+    nr = runestrlen(rp);
+    insertcharacter(t, nr, rp, r);
+}
+
+void
+selecthandler(Text *t, Rune r)
+{
+    if(t->eq0 != ~0)
+        textsetselect(t, t->eq0, t->q0);
+    if(t->ncache > 0)
+        typecommit(t);
+}
+
+void
+nlhandler(Text *t, Rune r)
+{
+    int nnb, nr = 1, i;
+    Rune* rp = &r;
+
+    if(t->w->autoindent){
+        /* find beginning of previous line using backspace code */
+        nnb = textbswidth(t, 0x15); /* ^U case */
+        rp = runemalloc(nnb + 1);
+        nr = 0;
+        rp[nr++] = r;
+        for(i=0; i<nnb; i++){
+            r = textreadc(t, t->q0-nnb+i);
+            if(r != ' ' && r != '\t')
+                break;
+            rp[nr++] = r;
+        }
+    }
+    typecommit(t);
+    insertcharacter(t, nr, rp, r);
+    if (rp != &r){
+        free(rp);
+    }
+}
+
+void
+extendedhandler(Text *t, Rune r)
+{
+    t->extended = t->extended ? FALSE : TRUE;
+}
+
+void
+wheeluphandler(Text *t, Rune r)
+{
+	caseup(t, mousescrollsize(t->Frame.maxlines));
+}
+
+void
+wheeldownhandler(Text *t, Rune r)
+{
+	int n = mousescrollsize(t->Frame.maxlines);
+	uint q0 = t->org+frcharofpt(&t->Frame, Pt(t->Frame.r.min.x, t->Frame.r.min.y+n*t->Frame.font->height));
+	textsetorigin(t, q0, TRUE);
+	return;
+}
+
+void
+texttype(Text *t, Rune r)
+{
+	int nr = 1, prevextended = t->extended;
+	Rune *rp;
+	Runestr dir;
+	char *aa;
+
+	if(t->what!=Body && r=='\n')
+		return;
+	nr = 1;
+	rp = &r;
+	struct Mapping *handler = findmapping((int)r, t->extended);
+	if (handler != nil) {
+	    if (handler->handler != nil){
+	        handler->handler(t, r);
+	    } else {
+	    	dir = dirname(t, nil, 0);
+        	aa = malloc(strlen(handler->external) + 1);
+        	strcpy(aa, handler->external);
+        	run(nil, aa, dir.r, dir.nr, TRUE, nil, nil, FALSE);
+	    }
+	} else {
+	    typecommit(t);
+	    insertcharacter(t, nr, rp, r);
+	}
+	if (prevextended){
+	    t->extended = FALSE;
+	}
+}
+
 void
 textcommit(Text *t, int tofile)
 {

+ 1 - 1
sys/src/cmd/zenith/util.c

@@ -59,7 +59,7 @@ cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
 void
 error(char *s)
 {
-	fprint(2, "acme: %s: %r\n", s);
+	fprint(2, "acme: %s(%x <- %x): %r\n", s,  __builtin_return_address(1),  __builtin_return_address(2));
 	remove(acmeerrorfile);
 	abort();
 }

+ 2 - 2
sys/src/cmd/zenith/wind.c

@@ -297,9 +297,9 @@ winsetname(Window *w, Rune *name, int n)
 	if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
 		return;
 	w->isscratch = FALSE;
-	if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6))
+	if(n>=6 && runeeq((Rune *)L"/guide", 6, name+(n-6), 6))
 		w->isscratch = TRUE;
-	else if(n>=7 && runeeq(L"+Errors", 7, name+(n-7), 7))
+	else if(n>=7 && runeeq((Rune *)L"+Errors", 7, name+(n-7), 7))
 		w->isscratch = TRUE;
 	filesetname(t->file, name, n);
 	for(i=0; i<t->file->ntext; i++){

+ 59 - 2
sys/src/cmd/zenith/xfid.c

@@ -25,12 +25,15 @@ enum
 	Ctlsize	= 5*12
 };
 
+void xfidglobalctlwrite(Xfid *x);
+
 char	Edel[]		= "deleted window";
-char	Ebadctl[]		= "ill-formed control message";
+char	Ebadctl[]	= "ill-formed control message";
 char	Ebadaddr[]	= "bad address syntax";
 char	Eaddr[]		= "address out of range";
-char	Einuse[]		= "already in use";
+char	Einuse[]	= "already in use";
 char	Ebadevent[]	= "bad event syntax";
+char	Ebadkeymap[] 	= "bad keymapping";
 extern char Eperm[];
 
 static
@@ -493,6 +496,10 @@ xfidwrite(Xfid *x)
 		t = &w->body;
 		goto BodyTag;
 
+	case Qctl:
+		xfidglobalctlwrite(x);
+		break;
+
 	case QWctl:
 		xfidctlwrite(x, w);
 		break;
@@ -584,6 +591,56 @@ xfidwrite(Xfid *x)
 		winunlock(w);
 }
 
+void
+xfidglobalctlwrite(Xfid *x)
+{
+	Fcall fc;
+	int n;
+	int isfbuf;
+	Rune *r;
+	char *err, *p;
+
+	err = nil;
+	if(x->Fcall.count < RBUFSIZE)
+		r = fbufalloc();
+	else{
+		isfbuf = FALSE;
+		r = emalloc(x->Fcall.count*UTFmax+1);
+	}
+	x->Fcall.data[x->Fcall.count] = 0;
+	p = strtok(x->Fcall.data, "\n");
+	while(p != nil){
+		char *token = strtok(p, " ");
+		if(strncmp(token, "keymap", 6) == 0){	// Add ctrl-? key mapping
+			char* key = strtok(0, " ");
+			char* mapping = strtok(0, " ");
+			if (keymap(key, mapping)) {
+				err = Ebadkeymap;
+				break;
+			}
+		} else if(strncmp(token, "extendedkeymap", 14) == 0){	// Add esc/ctrl-? key mapping
+			char* key = strtok(0, " ");
+			char* mapping = strtok(0, "\n");
+			if(extendedkeymap(key, mapping)){
+				err = Ebadkeymap;
+			        break;
+			}
+		} else {
+		        err = Ebadctl;
+		        break;
+		}
+		p = strtok(0, "\n");
+	}
+	if(isfbuf)
+		fbuffree(r);
+	else
+		free(r);
+        if(err)
+                n = 0;
+        fc.count = n;
+        respond(x, &fc, err);
+}
+
 void
 xfidctlwrite(Xfid *x, Window *w)
 {

+ 35 - 0
sys/src/cmd/zenith/zenith.init

@@ -0,0 +1,35 @@
+#!/bin/rc
+# Default zenith key mappings (with ctrl key)
+#
+echo keymap a start_of_line >/mnt/acme/ctl
+echo keymap w delete_word_before_cursor >/mnt/acme/ctl
+echo keymap u delete_to_start_of_line >/mnt/acme/ctl
+echo keymap h backspace >/mnt/acme/ctl
+echo keymap e end_of_line >/mnt/acme/ctl
+echo keymap left left >/mnt/acme/ctl
+echo keymap right right >/mnt/acme/ctl
+echo keymap down down >/mnt/acme/ctl
+echo keymap up up >/mnt/acme/ctl
+echo keymap pgup pgup >/mnt/acme/ctl
+echo keymap pgdown pgdown >/mnt/acme/ctl
+echo keymap home home >/mnt/acme/ctl
+echo keymap end end >/mnt/acme/ctl
+echo keymap ins ins >/mnt/acme/ctl
+echo keymap esc select >/mnt/acme/ctl
+echo keymap nl nl >/mnt/acme/ctl
+echo keymap wheelup wheelup >/mnt/acme/ctl
+echo keymap wheeldown wheeldown >/mnt/acme/ctl
+
+# Define extended key as ^s
+echo keymap s extend >/mnt/acme/ctl
+
+#
+# Extended key mappings (press ^s first)
+#
+echo extendedkeymap x execute_line >/mnt/acme/ctl
+echo extendedkeymap f /bin/fortune >/mnt/acme/ctl
+echo extendedkeymap l ls -l >/mnt/acme/ctl
+
+# Can cancel extended mode by pressing ^s
+echo extendedkeymap s extend >/mnt/acme/ctl
+

+ 8 - 1
sys/src/libc/port/malloc.c

@@ -170,7 +170,14 @@ ppanic(Pool *p, char *fmt, ...)
 		write(pv->printfd, "\n", 1);
 	}
 	va_end(v);
-//	unlock(&pv->lk);
+	n = seprint(msg, msg+sizeof(panicbuf), "stack: %x, %x, %x %x, %x, %x\n",
+	    __builtin_return_address(1), __builtin_return_address(2), __builtin_return_address(3),
+	    __builtin_return_address(4), __builtin_return_address(5), __builtin_return_address(6)
+	    ) - msg;
+	write(2, msg, n);
+	if(pv->printfd != 2){
+	    write(pv->printfd, msg, n);
+	}
 	abort();
 }
 

+ 30 - 0
usr/harvey/lib/zenith.init

@@ -0,0 +1,30 @@
+#!/bin/rc
+# Zenith key mappings (with ctrl key)
+echo keymap a start_of_line >/mnt/acme/ctl
+echo keymap w delete_word_before_cursor >/mnt/acme/ctl
+echo keymap u delete_to_start_of_line >/mnt/acme/ctl
+echo keymap h backspace >/mnt/acme/ctl
+echo keymap e end_of_line >/mnt/acme/ctl
+echo keymap left left >/mnt/acme/ctl
+echo keymap right right >/mnt/acme/ctl
+echo keymap down down >/mnt/acme/ctl
+echo keymap up up >/mnt/acme/ctl
+echo keymap pgup pgup >/mnt/acme/ctl
+echo keymap pgdown pgdown >/mnt/acme/ctl
+echo keymap home home >/mnt/acme/ctl
+echo keymap end end >/mnt/acme/ctl
+echo keymap ins ins >/mnt/acme/ctl
+echo keymap esc select >/mnt/acme/ctl
+echo keymap nl nl >/mnt/acme/ctl
+echo keymap s extend >/mnt/acme/ctl
+
+#
+# Extended key mappings (press ^s first)
+#
+echo extendedkeymap x execute_line >/mnt/acme/ctl
+echo extendedkeymap f /bin/fortune >/mnt/acme/ctl
+echo extendedkeymap l ls -l >/mnt/acme/ctl
+
+# Can cancel extended mode by pressing ^s
+echo extendedkeymap s extend >/mnt/acme/ctl
+