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 |