about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorbubstance <bubstance@tilde.institute>2023-11-14 23:02:29 +0000
committerbubstance <bubstance@tilde.institute>2023-11-14 23:02:29 +0000
commit47c8085520bc848acfadeff71398f6e3aa4285c5 (patch)
treeea305da23b5025094bf97e8aa8c898886e72b32a
downloadacme3k-47c8085520bc848acfadeff71398f6e3aa4285c5.tar.gz
initial commit HEAD master
-rw-r--r--LICENSE31
-rw-r--r--README.md54
-rw-r--r--acme3k.patch917
-rw-r--r--img/acme.pngbin0 -> 70127 bytes
-rw-r--r--img/suckme.pngbin0 -> 160987 bytes
5 files changed, 1002 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..b30ec57
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright © 2021 Plan 9 Foundation
+Portions Copyright © 2001-2008 Russ Cox
+Portions Copyright © 2008-2009 Google Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+===================================================================
+
+This software is derived from Plan 9, originally copyright Lucent Technologies
+and distributed under the Lucent Public License, Version 1.02.
+Lucent was bought by Alcatel, which was bought by Nokia.
+On March 23, 2021, Nokia announced the transfer of the Plan 9 copyrights to the
+Plan 9 Foundation, which in turn relicensed Plan 9 under the the MIT license,
+reproduced above.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..48f7a43
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# acme3k
+a continuation of [acme2k](https://github.com/karahobny/acme2k)
+
+![acme](img/acme.png)
+### [ACME](http://acme.cat-v.org/) INTERNATIONAL COMPILED EDITORS
+
+#### THEY EDIT LIKE HELL.
+```
+   No editor made, pretty much anywhere,
+
+surpasses our Acme in shape, material or finish.
+```
+
+## installation
+
+grab the patch, put it in your `$PLAN9` directory, apply, `mk install`
+
+```
+# cp acme3k.patch $PLAN9/src/cmd/acme/
+# cd $PLAN9/src/cmd/acme
+# patch -p1 < acme3k.patch
+# mk clean install
+```
+
+## features
+
+![suckme](img/suckme.png)
+
++ a centralized `config.h` header heavily inspired by the [suckless](https://suckless.org) design philosophy with the following options:
+
+  + autoindent, scroll button behavior, fonts, and colors modifiable by the user
+
+  + `bartflag`  removed as a runtime flag and moved to a configurable option allowing for click-to-focus operation
+
++ some keybindings that behave like you expect from other editors/paradigms:
+
+  + up/down arrows move between lines instead of scrolling the view
+
+  + `ctrl+c`/`ctrl+x`/`ctrl+v` for snarfing, cutting, and pasting selected text; `ctrl+z`/`ctrl+y` for undo/redo
+
+  + `home`/`end` move the cursor to the start/end of the current line, as do the original keybindings `ctrl+a`/`ctrl+e`
+
+  + `delete` removes one character forward
+
+### config.h
+`config.h` includes all the neccesary color and font modifications; just `mk install` whenever you modify it
+
+#### fonts
+run `fontsrv -p .` to list all the available fonts
+
+choose two fonts for `config.h`: the first one is treated as a proportional width font and is used everywhere in `acme`, the second one can be activated for a specific window by executing `Font` from its tag
+
+#### colors
+colors need to be in the format of `0x*rgb hex color code*FF` without the prefixed hashtag
diff --git a/acme3k.patch b/acme3k.patch
new file mode 100644
index 0000000..292f87c
--- /dev/null
+++ b/acme3k.patch
@@ -0,0 +1,917 @@
+diff --git a/src/cmd/acme/acme.c b/src/cmd/acme/acme.c
+index 0e6bc0fd..bb3aa502 100644
+--- a/src/cmd/acme/acme.c
++++ b/src/cmd/acme/acme.c
+@@ -11,9 +11,11 @@
+ #include <libsec.h>
+ #include "dat.h"
+ #include "fns.h"
+-	/* for generating syms in mkfile only: */
+-	#include <bio.h>
+-	#include "edit.h"
++#include "config.h"
++
++/* for generating syms in mkfile only: */
++#include <bio.h>
++#include "edit.h"
+ 
+ void	mousethread(void*);
+ void	keyboardthread(void*);
+@@ -21,28 +23,21 @@ void	waitthread(void*);
+ void	xfidallocthread(void*);
+ void	newwindowthread(void*);
+ void	plumbproc(void*);
+-int	timefmt(Fmt*);
++int		timefmt(Fmt*);
+ 
+ Reffont	**fontcache;
+ int		nfontcache;
+-char		wdir[512] = ".";
++char	wdir[512] = ".";
+ Reffont	*reffonts[2];
+ int		snarffd = -1;
+ int		mainpid;
+-int		swapscrollbuttons = FALSE;
+-char		*mtpt;
++char	*mtpt;
+ 
+ enum{
+ 	NSnarf = 1000	/* less than 1024, I/O buffer size */
+ };
+ Rune	snarfrune[NSnarf+1];
+ 
+-char		*fontnames[2] =
+-{
+-	"/lib/font/bit/lucsans/euro.8.font",
+-	"/lib/font/bit/lucm/unicode.9.font"
+-};
+-
+ Command *command;
+ 
+ void	shutdownthread(void*);
+@@ -65,7 +60,6 @@ threadmain(int argc, char *argv[])
+ 	Column *c;
+ 	int ncol;
+ 	Display *d;
+-
+ 	rfork(RFENVG|RFNAMEG);
+ 
+ 	ncol = -1;
+@@ -78,11 +72,26 @@ threadmain(int argc, char *argv[])
+ 		}
+ 		break;
+ 	case 'a':
+-		globalautoindent = TRUE;
++		if(globalautoindent)
++			globalautoindent = FALSE;
++		else
++			globalautoindent = TRUE;
+ 		break;
+-	case 'b':
++
++/*  bartmode/flag is now an option to be turned on config.h, just
++ *  because it is way too useful to leave it as an meaningless
++ *  flag, especially since considering how almost everyone seems
++ *  to even miss its existence.
++ *
++ *  to get to the point, it denies window focus following mouse.
++ *  how fickle those mice can truly be when decieveth by a soothing
++ *  treat, a teat or two.
++ */
++
++/*	case 'b':
+ 		bartflag = TRUE;
+-		break;
++		break; */
++
+ 	case 'c':
+ 		p = ARGF();
+ 		if(p == nil)
+@@ -393,7 +402,6 @@ acmeerrorproc(void *v)
+ 		buf[n] = '\0';
+ 		sendp(cerr, estrdup(buf));
+ 	}
+-	free(buf);
+ }
+ 
+ void
+@@ -547,7 +555,7 @@ mousethread(void *v)
+ 		case MResize:
+ 			if(getwindow(display, Refnone) < 0)
+ 				error("attach to window");
+-			draw(screen, screen->r, display->white, nil, ZP);
++			draw(screen, screen->r, winbgcol, nil, ZP);
+ 			iconinit();
+ 			scrlresize();
+ 			rowresize(&row, screen->clipr);
+@@ -964,74 +972,6 @@ Cursor boxcursor = {
+ 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
+ };
+ 
+-Cursor2 boxcursor2 = {
+-	{-15, -15},
+-	{0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xC0, 0x03, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF,
+-	 0xFF, 0xFF, 0xFF, 0xFF},
+-	{0x00, 0x00, 0x00, 0x00,
+-	 0x00, 0x00, 0x00, 0x00,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0x00, 0x00, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x3F, 0xFF, 0xFF, 0xFC,
+-	 0x00, 0x00, 0x00, 0x00,
+-	 0x00, 0x00, 0x00, 0x00}
+-};
+-
+ void
+ iconinit(void)
+ {
+@@ -1039,22 +979,32 @@ iconinit(void)
+ 	Image *tmp;
+ 
+ 	if(tagcols[BACK] == nil) {
+-		/* Blue */
+-		tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
+-		tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
+-		tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+-		tagcols[TEXT] = display->black;
+-		tagcols[HTEXT] = display->black;
+-
+-		/* Yellow */
+-		textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
+-		textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
+-		textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
+-		textcols[TEXT] = display->black;
+-		textcols[HTEXT] = display->black;
++#ifndef ENABLE_PASTELS
++		tagcols[BACK]	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TAGBG);
++#else
++		tagcols[BACK] = allocimagemix(display, C_TAGBG, DWhite);
++#endif
++
++		tagcols[HIGH]	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TAGHLBG);
++		tagcols[BORD]	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_COLBUTTON);
++		tagcols[TEXT]	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TAGFG);
++		tagcols[HTEXT]	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TAGHLFG);
++#ifndef ENABLE_PASTELS
++		textcols[BACK] 	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TXTBG);
++#else
++		textcols[BACK] = allocimagemix(display, C_TXTBG, DWhite);
++#endif
++		textcols[HIGH] 	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TXTHLBG);
++		textcols[BORD] 	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_SCROLLBG);
++		textcols[TEXT] 	= allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TXTFG);
++		textcols[HTEXT] = allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TXTHLFG);
++
+ 	}
+ 
+-	r = Rect(0, 0, Scrollwid, font->height+1);
++	winbgcol = allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_WINBG);
++	winbordercol = allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_WINBORDER);
++
++	r = Rect(0, 0, Scrollwid+ButtonBorder, font->height+1);
+ 	if(button && eqrect(r, button->r))
+ 		return;
+ 
+@@ -1066,22 +1016,24 @@ iconinit(void)
+ 
+ 	button = allocimage(display, r, screen->chan, 0, DNofill);
+ 	draw(button, r, tagcols[BACK], nil, r.min);
++	r.max.x -= ButtonBorder;
+ 	border(button, r, ButtonBorder, tagcols[BORD], ZP);
+ 
+ 	r = button->r;
+ 	modbutton = allocimage(display, r, screen->chan, 0, DNofill);
+ 	draw(modbutton, r, tagcols[BACK], nil, r.min);
++	r.max.x -= ButtonBorder;
+ 	border(modbutton, r, ButtonBorder, tagcols[BORD], ZP);
+ 	r = insetrect(r, ButtonBorder);
+-	tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
++	tmp = allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_TMPBUTTON);
+ 	draw(modbutton, r, tmp, nil, ZP);
+ 	freeimage(tmp);
+ 
+ 	r = button->r;
+-	colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
++	colbutton = allocimage(display, r, RGBA32, 1, C_WINBUTTON);
+ 
+-	but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
+-	but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
++	but2col = allocimage(display, r, screen->chan, 1, C_BUTTON2HL);
++	but3col = allocimage(display, r, screen->chan, 1, C_BUTTON3HL);
+ }
+ 
+ /*
+diff --git a/src/cmd/acme/addr.c b/src/cmd/acme/addr.c
+index 6aee8993..2ccae960 100644
+--- a/src/cmd/acme/addr.c
++++ b/src/cmd/acme/addr.c
+@@ -240,12 +240,12 @@ address(uint showerr, Text *t, Range lim, Range ar, void *a, uint q0, uint q1, i
+ 		case '5': case '6': case '7': case '8': case '9':
+ 			n = c -'0';
+ 			while(q<q1){
+-				nc = (*getc)(a, q++);
+-				if(nc<'0' || '9'<nc){
++				c = (*getc)(a, q++);
++				if(c<'0' || '9'<c){
+ 					q--;
+ 					break;
+ 				}
+-				n = n*10+(nc-'0');
++				n = n*10+(c-'0');
+ 			}
+ 			if(*evalp)
+ 				r = number(showerr, t, r, n, dir, size, evalp);
+diff --git a/src/cmd/acme/cols.c b/src/cmd/acme/cols.c
+index a53280c4..72572a39 100644
+--- a/src/cmd/acme/cols.c
++++ b/src/cmd/acme/cols.c
+@@ -29,7 +29,7 @@ colinit(Column *c, Rectangle r)
+ 	Rectangle r1;
+ 	Text *t;
+ 
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, winbgcol, nil, ZP);
+ 	c->r = r;
+ 	c->w = nil;
+ 	c->nw = 0;
+@@ -42,7 +42,7 @@ colinit(Column *c, Rectangle r)
+ 	t->what = Columntag;
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, winbordercol, nil, ZP);
+ 	textinsert(t, 0, Lheader, 38, TRUE);
+ 	textsetselect(t, t->file->b.nc, t->file->b.nc);
+ 	draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
+@@ -117,7 +117,7 @@ coladd(Column *c, Window *w, Window *clone, int y)
+ 		r1.max.y = min(y, v->body.fr.r.min.y+v->body.fr.nlines*v->body.fr.font->height);
+ 		r1.min.y = winresize(v, r1, FALSE, FALSE);
+ 		r1.max.y = r1.min.y+Border;
+-		draw(screen, r1, display->black, nil, ZP);
++		draw(screen, r1, winbordercol, nil, ZP);
+ 
+ 		/*
+ 		 * leave r with w's coordinates
+@@ -184,7 +184,7 @@ colclose(Column *c, Window *w, int dofree)
+ 	memmove(c->w+i, c->w+i+1, (c->nw-i)*sizeof(Window*));
+ 	c->w = realloc(c->w, c->nw*sizeof(Window*));
+ 	if(c->nw == 0){
+-		draw(screen, r, display->white, nil, ZP);
++		draw(screen, r, winbgcol, nil, ZP);
+ 		return;
+ 	}
+ 	up = 0;
+@@ -244,27 +244,28 @@ colresize(Column *c, Rectangle r)
+ 	r1.max.y = r1.min.y + c->tag.fr.font->height;
+ 	textresize(&c->tag, r1, TRUE);
+ 	draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
++	new = Dy(r) - c->nw*(Border + font->height);
++	old = Dy(c->r) - c->nw*(Border + font->height);
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, winbordercol, nil, ZP);
+ 	r1.max.y = r.max.y;
+-	new = Dy(r) - c->nw*(Border + font->height);
+-	old = Dy(c->r) - c->nw*(Border + font->height);
+ 	for(i=0; i<c->nw; i++){
+ 		w = c->w[i];
+ 		w->maxlines = 0;
+ 		if(i == c->nw-1)
+ 			r1.max.y = r.max.y;
+-		else{
++		else {
+ 			r1.max.y = r1.min.y;
+ 			if(new > 0 && old > 0 && Dy(w->r) > Border+font->height){
+ 				r1.max.y += (Dy(w->r)-Border-font->height)*new/old + Border + font->height;
+ 			}
++			r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r);
+ 		}
+ 		r1.max.y = max(r1.max.y, r1.min.y + Border+font->height);
+ 		r2 = r1;
+ 		r2.max.y = r2.min.y+Border;
+-		draw(screen, r2, display->black, nil, ZP);
++		draw(screen, r2, winbordercol, nil, ZP);
+ 		r1.min.y = r2.max.y;
+ 		r1.min.y = winresize(w, r1, FALSE, i==c->nw-1);
+ 	}
+@@ -320,7 +321,7 @@ colsort(Column *c)
+ 			r.max.y = r.min.y+Dy(w->r)+Border;
+ 		r1 = r;
+ 		r1.max.y = r1.min.y+Border;
+-		draw(screen, r1, display->black, nil, ZP);
++		draw(screen, r1, winbordercol, nil, ZP);
+ 		r.min.y = r1.max.y;
+ 		y = winresize(w, r, FALSE, i==c->nw-1);
+ 	}
+@@ -417,8 +418,8 @@ colgrow(Column *c, Window *w, int but)
+ 		if(nl[j])
+ 			r.max.y += 1 + nl[j]*v->body.fr.font->height;
+ 		r.min.y = winresize(v, r, c->safe, FALSE);
+-		r.max.y = r.min.y + Border;
+-		draw(screen, r, display->black, nil, ZP);
++		r.max.y += Border;
++		draw(screen, r, winbordercol, nil, ZP);
+ 		y1 = r.max.y;
+ 	}
+ 	/* scan to see new size of everyone below */
+@@ -445,7 +446,7 @@ colgrow(Column *c, Window *w, int but)
+ 	if(i < c->nw-1){
+ 		r.min.y = r.max.y;
+ 		r.max.y += Border;
+-		draw(screen, r, display->black, nil, ZP);
++		draw(screen, r, winbordercol, nil, ZP);
+ 		for(j=i+1; j<c->nw; j++)
+ 			ny[j] -= (y2-r.max.y);
+ 	}
+@@ -462,7 +463,7 @@ colgrow(Column *c, Window *w, int but)
+ 		if(j < c->nw-1){	/* no border on last window */
+ 			r.min.y = y1;
+ 			r.max.y += Border;
+-			draw(screen, r, display->black, nil, ZP);
++			draw(screen, r, winbordercol, nil, ZP);
+ 			y1 = r.max.y;
+ 		}
+ 	}
+@@ -482,7 +483,7 @@ coldragwin(Column *c, Window *w, int but)
+ 	Column *nc;
+ 
+ 	clearmouse();
+-	setcursor2(mousectl, &boxcursor, &boxcursor2);
++	setcursor(mousectl, &boxcursor);
+ 	b = mouse->buttons;
+ 	op = mouse->xy;
+ 	while(mouse->buttons == b)
+@@ -544,7 +545,7 @@ coldragwin(Column *c, Window *w, int but)
+ 	}
+ 	r.min.y = winresize(v, r, c->safe, FALSE);
+ 	r.max.y = r.min.y+Border;
+-	draw(screen, r, display->black, nil, ZP);
++	draw(screen, r, winbordercol, nil, ZP);
+ 	r.min.y = r.max.y;
+ 	if(i == c->nw-1)
+ 		r.max.y = c->r.max.y;
+diff --git a/src/cmd/acme/config.h b/src/cmd/acme/config.h
+new file mode 100644
+index 00000000..104f9c98
+--- /dev/null
++++ b/src/cmd/acme/config.h
+@@ -0,0 +1,32 @@
++char *fontnames[2] = {
++	"/mnt/font/DejaVuSansMono/11a/font",
++	"/mnt/font/DejaVuSansMono/18a/font",
++};
++
++int bartflag			= FALSE;   // click-to-focus (experimental)
++int globalautoindent	= TRUE;    // default auto-indent
++int	swapscrollbuttons	= FALSE;   // TRUE = 1:down/3:up
++
++#define C_TAGBG         0x222222FF // tag window bg
++#define C_TAGHLBG       0x444444FF // tag window highlighted bg
++
++#define C_TAGFG         0xBBBBBBFF // tag window fg
++#define C_TAGHLFG       0xEEEEEEFF // tag window highlighted fg
++
++#define C_TXTBG         0x000000FF // text window bg
++#define C_TXTHLBG       0x444444FF // text window highlighted bg
++
++#define C_TXTFG         0xEEEEEEFF // text window fg
++#define C_TXTHLFG       0xEEEEEEFF // text window highlighted fg
++
++#define C_WINBG         0x111111FF // empty column bg
++#define C_WINBORDER     0x111111FF // separator between tags/windows
++#define C_WINBUTTON     0x005577FF // window frame decorations
++
++#define C_COLBUTTON     0x005577FF // column button
++#define C_TMPBUTTON     0x55AAAAFF // modified window button (inside WINBUTTON)
++
++#define C_SCROLLBG      0x222222FF // scrollbar bg
++
++#define C_BUTTON2HL     0xCC3333FF // button 2 drag highlight bg
++#define C_BUTTON3HL     0x005577FF // button 3 drag highlight bg
+diff --git a/src/cmd/acme/dat.h b/src/cmd/acme/dat.h
+index e540605a..abab2fdc 100644
+--- a/src/cmd/acme/dat.h
++++ b/src/cmd/acme/dat.h
+@@ -524,8 +524,9 @@ Image		*colbutton;
+ Image		*button;
+ Image		*but2col;
+ Image		*but3col;
++Image		*winbgcol;
++Image		*winbordercol;
+ Cursor		boxcursor;
+-Cursor2		boxcursor2;
+ Row			row;
+ int			timerpid;
+ Disk			*disk;
+diff --git a/src/cmd/acme/ecmd.c b/src/cmd/acme/ecmd.c
+index f7613172..be580234 100644
+--- a/src/cmd/acme/ecmd.c
++++ b/src/cmd/acme/ecmd.c
+@@ -582,8 +582,6 @@ x_cmd(Text *t, Cmd *cp)
+ int
+ X_cmd(Text *t, Cmd *cp)
+ {
+-	USED(t);
+-
+ 	filelooper(t, cp, cp->cmdc=='X');
+ 	return TRUE;
+ }
+@@ -1002,14 +1000,16 @@ filelooper(Text *t, Cmd *cp, int XY)
+ 	 */
+ 	allwindows(alllocker, (void*)1);
+ 	globalincref = 1;
+-	
++	for(i=0; i<loopstruct.nw; i++)
++		cmdexec(&loopstruct.w[i]->body, cp->u.cmd);
++
+ 	/*
+ 	 * Unlock the window running the X command.
+ 	 * We'll need to lock and unlock each target window in turn.
+ 	 */
+ 	if(t && t->w)
+ 		winunlock(t->w);
+-	
++
+ 	for(i=0; i<loopstruct.nw; i++) {
+ 		targ = &loopstruct.w[i]->body;
+ 		if(targ && targ->w)
+diff --git a/src/cmd/acme/exec.c b/src/cmd/acme/exec.c
+index 1dd02288..cd2e36b2 100644
+--- a/src/cmd/acme/exec.c
++++ b/src/cmd/acme/exec.c
+@@ -768,7 +768,7 @@ putfile(File *f, int q0, int q1, Rune *namer, int nname)
+ 	b = nil;
+ 	if(ret < 0 || retc < 0) {
+ 		warning(nil, "can't write file %s: %r\n", name);
+-		goto Rescue2; // flush or close failed
++		goto Rescue2;
+ 	}
+ 	if(runeeq(namer, nname, f->name, f->nname)){
+ 		if(q0!=0 || q1!=f->b.nc){
+diff --git a/src/cmd/acme/look.c b/src/cmd/acme/look.c
+index a7172b50..6fe18974 100644
+--- a/src/cmd/acme/look.c
++++ b/src/cmd/acme/look.c
+@@ -491,11 +491,12 @@ dirname(Text *t, Rune *r, int n)
+ 	if(n>=1 && r[0]=='/')
+ 		goto Rescue;
+ 	b = parsetag(t->w, n, &i);
++	bufread(&t->w->tag.file->b, 0, b, nt);
+ 	slash = -1;
+ 	for(i--; i >= 0; i--){
+-		if(b[i] == '/'){
+-			slash = i;
+-			break;
++	        if(b[i] == '/'){
++		        slash = i;
++		        break;
+ 		}
+ 	}
+ 	if(slash < 0)
+diff --git a/src/cmd/acme/mkfile b/src/cmd/acme/mkfile
+index 18bea9e0..2bad712c 100644
+--- a/src/cmd/acme/mkfile
++++ b/src/cmd/acme/mkfile
+@@ -29,6 +29,7 @@ OFILES=\
+ HFILES=dat.h\
+ 	edit.h\
+ 	fns.h\
++	config.h
+ 
+ <$PLAN9/src/mkone
+ <$PLAN9/src/mkdirs
+diff --git a/src/cmd/acme/rows.c b/src/cmd/acme/rows.c
+index 7a64fabf..93445861 100644
+--- a/src/cmd/acme/rows.c
++++ b/src/cmd/acme/rows.c
+@@ -28,7 +28,7 @@ rowinit(Row *row, Rectangle r)
+ 	Rectangle r1;
+ 	Text *t;
+ 
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, winbgcol, nil, ZP);
+ 	row->r = r;
+ 	row->col = nil;
+ 	row->ncol = 0;
+@@ -42,7 +42,7 @@ rowinit(Row *row, Rectangle r)
+ 	t->col = nil;
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, winbordercol, nil, ZP);
+ 	textinsert(t, 0, Lcolhdr, 29, TRUE);
+ 	textsetselect(t, t->file->b.nc, t->file->b.nc);
+ }
+@@ -73,7 +73,7 @@ rowadd(Row *row, Column *c, int x)
+ 		r = d->r;
+ 		if(Dx(r) < 100)
+ 			return nil;
+-		draw(screen, r, display->white, nil, ZP);
++		draw(screen, r, winbgcol, nil, ZP);
+ 		r1 = r;
+ 		r1.max.x = min(x-Border, r.max.x-50);
+ 		if(Dx(r1) < 50)
+@@ -81,7 +81,7 @@ rowadd(Row *row, Column *c, int x)
+ 		colresize(d, r1);
+ 		r1.min.x = r1.max.x;
+ 		r1.max.x = r1.min.x+Border;
+-		draw(screen, r1, display->black, nil, ZP);
++		draw(screen, r1, winbordercol, nil, ZP);
+ 		r.min.x = r1.max.x;
+ 	}
+ 	if(c == nil){
+@@ -115,7 +115,7 @@ rowresize(Row *row, Rectangle r)
+ 	textresize(&row->tag, r1, TRUE);
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, winbordercol, nil, ZP);
+ 	r.min.y = r1.max.y;
+ 	r1 = r;
+ 	r1.max.x = r1.min.x;
+@@ -130,7 +130,7 @@ rowresize(Row *row, Rectangle r)
+ 		if(i > 0){
+ 			r2 = r1;
+ 			r2.max.x = r2.min.x+Border;
+-			draw(screen, r2, display->black, nil, ZP);
++			draw(screen, r2, winbordercol, nil, ZP);
+ 			r1.min.x = r2.max.x;
+ 		}
+ 		colresize(c, r1);
+@@ -148,7 +148,7 @@ rowdragcol(Row *row, Column *c, int _0)
+ 	USED(_0);
+ 
+ 	clearmouse();
+-	setcursor2(mousectl, &boxcursor, &boxcursor2);
++	setcursor(mousectl, &boxcursor);
+ 	b = mouse->buttons;
+ 	op = mouse->xy;
+ 	while(mouse->buttons == b)
+@@ -191,14 +191,14 @@ rowdragcol(Row *row, Column *c, int _0)
+ 		p.x = c->r.max.x-80-Scrollwid;
+ 	r = d->r;
+ 	r.max.x = c->r.max.x;
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, winbgcol, nil, ZP);
+ 	r.max.x = p.x;
+ 	colresize(d, r);
+ 	r = c->r;
+ 	r.min.x = p.x;
+ 	r.max.x = r.min.x;
+ 	r.max.x += Border;
+-	draw(screen, r, display->black, nil, ZP);
++	draw(screen, r, winbordercol, nil, ZP);
+ 	r.min.x = r.max.x;
+ 	r.max.x = c->r.max.x;
+ 	colresize(c, r);
+@@ -223,7 +223,7 @@ rowclose(Row *row, Column *c, int dofree)
+ 	memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
+ 	row->col = realloc(row->col, row->ncol*sizeof(Column*));
+ 	if(row->ncol == 0){
+-		draw(screen, r, display->white, nil, ZP);
++		draw(screen, r, winbgcol, nil, ZP);
+ 		return;
+ 	}
+ 	if(i == row->ncol){		/* extend last column right */
+@@ -234,7 +234,7 @@ rowclose(Row *row, Column *c, int dofree)
+ 		c = row->col[i];
+ 		r.max.x = c->r.max.x;
+ 	}
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, winbgcol, nil, ZP);
+ 	colresize(c, r);
+ }
+ 
+@@ -592,12 +592,12 @@ rowload(Row *row, char *file, int initing)
+ 			r2.min.x = x;
+ 			if(Dx(r1) < 50 || Dx(r2) < 50)
+ 				continue;
+-			draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
++			draw(screen, Rpt(r1.min, r2.max), winbgcol, nil, ZP);
+ 			colresize(c1, r1);
+ 			colresize(c2, r2);
+ 			r2.min.x = x-Border;
+ 			r2.max.x = x;
+-			draw(screen, r2, display->black, nil, ZP);
++			draw(screen, r2, winbordercol, nil, ZP);
+ 		}
+ 		if(i >= row->ncol)
+ 			rowadd(row, nil, x);
+diff --git a/src/cmd/acme/text.c b/src/cmd/acme/text.c
+index 09422dda..92404ddc 100644
+--- a/src/cmd/acme/text.c
++++ b/src/cmd/acme/text.c
+@@ -542,6 +542,8 @@ textbswidth(Text *t, Rune c)
+ 	/* there is known to be at least one character to erase */
+ 	if(c == 0x08)	/* ^H: erase character */
+ 		return 1;
++	if(c == 0x7F)	/* DEL */
++		return 1;
+ 	q = t->q0;
+ 	skipping = TRUE;
+ 	while(q > 0){
+@@ -691,11 +693,6 @@ texttype(Text *t, Rune r)
+ 		if(t->q1 < t->file->b.nc)
+ 			textshow(t, t->q1+1, t->q1+1, TRUE);
+ 		return;
+-	case Kdown:
+-		if(t->what == Tag)
+-			goto Tagdown;
+-		n = t->fr.maxlines/3;
+-		goto case_Down;
+ 	case Kscrollonedown:
+ 		if(t->what == Tag)
+ 			goto Tagdown;
+@@ -709,11 +706,6 @@ texttype(Text *t, Rune r)
+ 		q0 = t->org+frcharofpt(&t->fr, Pt(t->fr.r.min.x, t->fr.r.min.y+n*t->fr.font->height));
+ 		textsetorigin(t, q0, TRUE);
+ 		return;
+-	case Kup:
+-		if(t->what == Tag)
+-			goto Tagup;
+-		n = t->fr.maxlines/3;
+-		goto case_Up;
+ 	case Kscrolloneup:
+ 		if(t->what == Tag)
+ 			goto Tagup;
+@@ -725,27 +717,61 @@ texttype(Text *t, Rune r)
+ 		q0 = textbacknl(t, t->org, n);
+ 		textsetorigin(t, q0, TRUE);
+ 		return;
+-	case Khome:
++
++/*
++ *  Keybindings for moving the cursor up and
++ *  down the text via up and down arrow keys.
++ */
++
++	case Kdown:
++		if(t->what == Tag)
++			goto Tagdown;
+ 		typecommit(t);
+-		if(t->org > t->iq1) {
+-			q0 = textbacknl(t, t->iq1, 1);
+-			textsetorigin(t, q0, TRUE);
+-		} else
+-			textshow(t, 0, 0, FALSE);
++		/* 1rst check for being in the last line*/
++		q0 = t->q0;
++		q1 = q0;
++		if (q1) q1--;
++		nnb = 0;
++		while(q0<t->file->b.nc && textreadc(t, q0)!='\n')
++			q0++;
++		if (q0 == (t->file->b.nc)-1) {
++			textshow(t, q0, q0, TRUE);
++			return;
++		}
++		q0++;
++		/* find old pos in ln */
++		while(q1>1 && textreadc(t, q1)!='\n'){
++			nnb++;
++			q1--;
++		}
++		/* go right until reachg pos or \n */
++		while(q0<t->file->b.nc && (nnb>0 && textreadc(t, q0)!='\n')){
++			q0++;
++			nnb--;
++		}
++		if (q0>1 && q0<t->file->b.nc)
++			textshow(t, q0, q0, TRUE);
+ 		return;
+-	case Kend:
++	case Kup:
++		if(t->what == Tag)
++			goto Tagup;
+ 		typecommit(t);
+-		if(t->iq1 > t->org+t->fr.nchars) {
+-			if(t->iq1 > t->file->b.nc) {
+-				// should not happen, but does. and it will crash textbacknl.
+-				t->iq1 = t->file->b.nc;
+-			}
+-			q0 = textbacknl(t, t->iq1, 1);
+-			textsetorigin(t, q0, TRUE);
+-		} else
+-			textshow(t, t->file->b.nc, t->file->b.nc, FALSE);
++		nnb = 0;
++		if(t->q0>0 && textreadc(t, t->q0-1)!='\n')
++			nnb = textbswidth(t, 0x15);
++		/* BOL - 1 if not first line of txt BOL*/
++		if( t->q0-nnb > 1  && textreadc(t, t->q0-nnb-1)=='\n' ) nnb++;
++		textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
+ 		return;
+-	case 0x01:	/* ^A: beginning of line */
++
++/*
++ *  Home and End now respectively take you to the
++ *  beginning and to the end of the line respectively.
++ *  Also keep the original ^A and ^E keybindings.
++ */
++
++	case 0x01:
++	case Khome:
+ 		typecommit(t);
+ 		/* go to where ^U would erase, if not already at BOL */
+ 		nnb = 0;
+@@ -753,13 +779,17 @@ texttype(Text *t, Rune r)
+ 			nnb = textbswidth(t, 0x15);
+ 		textshow(t, t->q0-nnb, t->q0-nnb, TRUE);
+ 		return;
+-	case 0x05:	/* ^E: end of line */
++	case 0x05:
++	case Kend:
+ 		typecommit(t);
+ 		q0 = t->q0;
+ 		while(q0<t->file->b.nc && textreadc(t, q0)!='\n')
+ 			q0++;
+ 		textshow(t, q0, q0, TRUE);
+ 		return;
++
++/* I'll keep the MAC-keybindings 'cuz im such a nice guy */
++
+ 	case Kcmd+'c':	/* %C: copy */
+ 		typecommit(t);
+ 		cut(t, t, nil, TRUE, FALSE, nil, 0);
+@@ -773,14 +803,32 @@ texttype(Text *t, Rune r)
+ 		undo(t, nil, nil, FALSE, 0, nil, 0);
+ 		return;
+ 
++/*
++ *  Adding Windows and X11 -compatible Ctrl+C, Ctrl+Z and
++ *  Ctrl+Y (for redoing). TODO: find out how to check out for
++ *  shift press, for Ctrl+Shift+Z in this godforsaken switch()
++ */
++
++	case 0x03:	/* Ctrl+C: copy */
++		typecommit(t);
++		cut(t, t, nil, TRUE, FALSE, nil, 0);
++		return;
++	case 0x1A:	/* Ctrl+Z: undo */
++		typecommit(t);
++		undo(t, nil, nil, TRUE, 0, nil, 0);
++		return;
++	case 0x19:	/* Ctrl+Y: redo */
++		typecommit(t);
++		undo(t, nil, nil, FALSE, 0, nil, 0);
++		return;
++
+ 	Tagdown:
+ 		/* expand tag to show all text */
+ 		if(!t->w->tagexpand){
+-			t->w->tagexpand = TRUE;
+-			winresize(t->w, t->w->r, FALSE, TRUE);
++		t->w->tagexpand = TRUE;
++		winresize(t->w, t->w->r, FALSE, TRUE);
+ 		}
+ 		return;
+-
+ 	Tagup:
+ 		/* shrink tag to single line */
+ 		if(t->w->tagexpand){
+@@ -790,11 +838,16 @@ texttype(Text *t, Rune r)
+ 		}
+ 		return;
+ 	}
++
++
++
+ 	if(t->what == Body){
+ 		seq++;
+ 		filemark(t->file);
+ 	}
++
+ 	/* cut/paste must be done after the seq++/filemark */
++
+ 	switch(r){
+ 	case Kcmd+'x':	/* %X: cut */
+ 		typecommit(t);
+@@ -816,7 +869,31 @@ texttype(Text *t, Rune r)
+ 		textshow(t, t->q0, t->q1, 1);
+ 		t->iq1 = t->q1;
+ 		return;
++
++	/* Same for Windows / X11 */
++
++	case 0x18:	/* Ctrl+X: cut */
++		typecommit(t);
++		if(t->what == Body){
++			seq++;
++			filemark(t->file);
++		}
++		cut(t, t, nil, TRUE, TRUE, nil, 0);
++		textshow(t, t->q0, t->q0, 1);
++		t->iq1 = t->q0;
++		return;
++	case 0x16:	/* Ctrl+V: paste */
++		typecommit(t);
++		if(t->what == Body){
++			seq++;
++			filemark(t->file);
++		}
++		paste(t, t, nil, TRUE, FALSE, nil, 0);
++		textshow(t, t->q0, t->q1, 1);
++		t->iq1 = t->q1;
++		return;
+ 	}
++
+ 	if(t->q1 > t->q0){
+ 		if(t->ncache != 0)
+ 			error("text.type");
+@@ -824,6 +901,7 @@ texttype(Text *t, Rune r)
+ 		t->eq0 = ~0;
+ 	}
+ 	textshow(t, t->q0, t->q0, 1);
++
+ 	switch(r){
+ 	case 0x06:	/* ^F: complete */
+ 	case Kins:
+@@ -844,9 +922,21 @@ texttype(Text *t, Rune r)
+ 			typecommit(t);
+ 		t->iq1 = t->q0;
+ 		return;
++
++	case 0x7F:  /* [delete key] */
++		/* ensure we're not at the end of the buffer */
++		if(t->q1 >= t->file->b.nc) {
++			return;
++		}
++		/* emulate rightarrow key */
++		typecommit(t);
++		textshow(t, t->q1+1, t->q1+1, TRUE);
++		/* emulate backspace key */
++		// r = 0x08; /* needed for textbswidth() to return correct value */
++					 /* now fall through to next case */
+ 	case 0x08:	/* ^H: erase character */
+ 	case 0x15:	/* ^U: erase line */
+-	case 0x17:	/* ^W: erase word */
++	case 0x17:	/* ^W: erase  word */
+ 		if(t->q0 == 0)	/* nothing to erase */
+ 			return;
+ 		nnb = textbswidth(t, r);
+diff --git a/src/cmd/acme/wind.c b/src/cmd/acme/wind.c
+index 9441bbb7..98c97368 100644
+--- a/src/cmd/acme/wind.c
++++ b/src/cmd/acme/wind.c
+@@ -235,7 +235,6 @@ winresize(Window *w, Rectangle r, int safe, int keepextra)
+ 			r1.min.y = min(y, r.max.y);
+ 			r1.max.y = r.max.y;
+ 		}else{
+-			draw(screen, r1, textcols[BACK], nil, ZP);
+ 			r1.min.y = y;
+ 			r1.max.y = y;
+ 		}
diff --git a/img/acme.png b/img/acme.png
new file mode 100644
index 0000000..4207e3f
--- /dev/null
+++ b/img/acme.png
Binary files differdiff --git a/img/suckme.png b/img/suckme.png
new file mode 100644
index 0000000..7f0394d
--- /dev/null
+++ b/img/suckme.png
Binary files differ