]> git.mdlowis.com Git - projs/tide.git/commitdiff
xedit.c is now 100% functional using the new win ui layer. unit tests no longer build...
authorMichael D. Lowis <mike.lowis@gentex.com>
Fri, 3 Feb 2017 14:08:10 +0000 (09:08 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Fri, 3 Feb 2017 14:08:10 +0000 (09:08 -0500)
inc/win.h
lib/view.c
lib/win.c
term.c
xedit.c

index 1c9b7feb5b6c705a020ffde8eb66dd872d36d044..684cbf0ddf98d67b56646262f595cdb2aebde115 100644 (file)
--- a/inc/win.h
+++ b/inc/win.h
@@ -36,7 +36,11 @@ void win_setmouse(MouseConfig* mconfig);
 View* win_view(WinRegion id);\r
 Buf* win_buf(WinRegion id);\r
 Sel* win_sel(WinRegion id);\r
+bool win_btnpressed(MouseBtn btn);\r
+WinRegion win_getregion(void);\r
+void win_setregion(WinRegion id);\r
 \r
+void onupdate(void);\r
 void mouse_left(WinRegion id, size_t count, size_t row, size_t col);\r
 void mouse_middle(WinRegion id, size_t count, size_t row, size_t col);\r
 void mouse_right(WinRegion id, size_t count, size_t row, size_t col);\r
index e01c036a334fa75fe47850132042e0ac47a34d99..be6dc24cf40ba44db0f8212886afa188e9d77658 100644 (file)
@@ -71,7 +71,7 @@ static size_t fill_row(View* view, unsigned row, size_t pos) {
 }
 
 static void reflow(View* view) {
-    if (!view->nrows) return;
+    if (!view->rows) return;
     size_t pos = view->rows[0]->off;
     for (size_t y = 0; y < view->nrows; y++)
         pos = fill_row(view, y, pos);
index b333781e4081db6d0e916c9cb57030f5773c6e5e..24309b7af99e074863c28305bb71953ef90ecd7c 100644 (file)
--- a/lib/win.c
+++ b/lib/win.c
@@ -1,8 +1,8 @@
 #include <stdc.h>\r
 #include <utf.h>\r
 #include <edit.h>\r
-#include <win.h>\r
 #include <x11.h>\r
+#include <win.h>\r
 \r
 static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols);\r
 static WinRegion getregion(size_t x, size_t y);\r
@@ -36,8 +36,7 @@ static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0};
 KeyBinding* Keys = NULL;\r
 \r
 void win_init(char* name) {\r
-    win_settext(STATUS, "This is a status line");\r
-    for (int i = 1; i < SCROLL; i++)\r
+    for (int i = 0; i < SCROLL; i++)\r
         view_init(&(Regions[i].view), NULL);\r
     x11_init(&Config);\r
     Font = x11_font_load(FONTNAME);\r
@@ -50,15 +49,31 @@ void win_loop(void) {
 }\r
 \r
 void win_settext(WinRegion id, char* text) {\r
-    view_init(win_view(id), NULL);\r
-    view_putstr(win_view(id), text);\r
-    view_selprev(win_view(id)); // clear the selection\r
+    View* view = win_view(id);\r
+    view->buffer.gapstart = view->buffer.bufstart;\r
+    view->buffer.gapend   = view->buffer.bufend;\r
+    view->selection = (Sel){0,0,0};\r
+    view_putstr(view, text);\r
+    view_selprev(view); // clear the selection\r
+    buf_logclear(&(view->buffer));\r
 }\r
 \r
 void win_setkeys(KeyBinding* bindings) {\r
     Keys = bindings;\r
 }\r
 \r
+bool win_btnpressed(MouseBtn btn) {\r
+    return MouseBtns[btn].pressed;\r
+}\r
+\r
+WinRegion win_getregion(void) {\r
+    return Focused;\r
+}\r
+\r
+void win_setregion(WinRegion id) {\r
+    Focused = id;\r
+}\r
+\r
 View* win_view(WinRegion id) {\r
     if (id == FOCUSED) id = Focused;\r
     return &(Regions[id].view);\r
@@ -114,7 +129,7 @@ static void layout(int width, int height) {
 static void onredraw(int width, int height) {\r
     size_t fheight = x11_font_height(Font);\r
     size_t fwidth  = x11_font_width(Font);\r
-    \r
+    onupdate(); // Let the user program update the status and such\r
     /* layout and draw the three text regions */\r
     layout(width, height);\r
     for (int i = 0; i < SCROLL; i++) {\r
@@ -151,7 +166,7 @@ static void oninput(int mods, Rune key) {
     if (key == '\n' && win_view(FOCUSED)->buffer.crlf) key = RUNE_CRLF;\r
     /* search for a key binding entry */\r
     uint32_t mkey = tolower(key);\r
-    for (KeyBinding* bind = Keys; bind && bind->mods; bind++) {\r
+    for (KeyBinding* bind = Keys; bind && bind->key; bind++) {\r
         if ((mkey == bind->key) && (bind->mods == ModAny || bind->mods == mods)) {\r
             bind->action();\r
             return;\r
@@ -166,18 +181,19 @@ static void oninput(int mods, Rune key) {
 \r
 static void onmouse(MouseAct act, MouseBtn btn, int x, int y) {\r
     WinRegion id = getregion(x, y);\r
-    if (id != TAGS && id != EDIT) return;\r
-    if (Focused != id) Focused = id;\r
     size_t row = (y-Regions[id].y) / x11_font_height(Font);\r
     size_t col = (x-Regions[id].x) / x11_font_width(Font);\r
     if (act == MOUSE_ACT_MOVE) {\r
         if (MouseBtns[MOUSE_BTN_LEFT].pressed) {\r
+            WinRegion selid = MouseBtns[MOUSE_BTN_LEFT].region;\r
             if (MouseBtns[MOUSE_BTN_LEFT].count == 1) {\r
-                view_setcursor(win_view(id), row, col);\r
+                view_setcursor(win_view(selid), row, col);\r
                 MouseBtns[MOUSE_BTN_LEFT].count = 0;\r
             } else {\r
-                view_selext(win_view(id), row, col);\r
+                view_selext(win_view(selid), row, col);\r
             }\r
+        } else if (id == TAGS || id == EDIT) {\r
+            Focused = id;\r
         }\r
     } else {\r
         MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN);\r
@@ -194,7 +210,7 @@ static void onmouse(MouseAct act, MouseBtn btn, int x, int y) {
         } else if (MouseBtns[btn].count > 0) {\r
             /* execute the action on button release */\r
             if (MouseActs[btn])\r
-                MouseActs[btn](id, MouseBtns[btn].count, row, col);\r
+                MouseActs[btn](MouseBtns[btn].region, MouseBtns[btn].count, row, col);\r
         }\r
     }\r
 }\r
@@ -248,4 +264,3 @@ static WinRegion getregion(size_t x, size_t y) {
     return NREGIONS;\r
 }\r
 \r
-\r
diff --git a/term.c b/term.c
index ca167f23f7b62254d3b995c9692b36914fb0263a..8c5a877c45b46f576e0a854e7d6a3dd7e48d552b 100644 (file)
--- a/term.c
+++ b/term.c
@@ -8,15 +8,15 @@
 /* Mouse Handling
  *****************************************************************************/
 void mouse_left(WinRegion id, size_t count, size_t row, size_t col) {
-    
+
 }
 
 void mouse_middle(WinRegion id, size_t count, size_t row, size_t col) {
-    
+
 }
 
 void mouse_right(WinRegion id, size_t count, size_t row, size_t col) {
-    
+
 }
 
 MouseConfig* MouseHandlers[NREGIONS] = {
@@ -32,6 +32,10 @@ MouseConfig* MouseHandlers[NREGIONS] = {
 
 /* Main Routine
  *****************************************************************************/
+void onupdate(void) {
+}
+
 int main(int argc, char** argv) {
     win_init("term");
     //win_setkeys(&Bindings);
@@ -65,7 +69,7 @@ int main(int argc, char** argv) {
     layout(width, height);
     x11_draw_rect(CLR_BASE03, 0, 0, width, height);
     x11_draw_rect(CLR_BASE02, 0, Regions[EDIT].y-2, fwidth + 4, height - Regions[EDIT].y + 2);
-    
+
     draw_status(CLR_BASE1, (width - 4) / x11_font_width(Font));
     draw_region(TAGS);
     draw_region(EDIT);
@@ -187,7 +191,7 @@ static KeyBinding Bindings[] = {
     { ModAny, KEY_DOWN,  cursor_dn    },
     { ModAny, KEY_LEFT,  cursor_left  },
     { ModAny, KEY_RIGHT, cursor_right },
-    
+
     /* Standard Unix Shortcuts */
     { ModCtrl, 'u', del_to_bol  },
     { ModCtrl, 'k', del_to_eol  },
@@ -336,23 +340,6 @@ static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t
 }
 
 static void draw_status(int fg, size_t ncols) {
-    Buf* buf = getbuf(EDIT);
-    UGlyph glyphs[ncols], *status = glyphs;
-    (status++)->rune = (buf->charset == BINARY ? 'B' : 'U');
-    (status++)->rune = (buf->crlf ? 'C' : 'N');
-    (status++)->rune = (buf->expand_tabs ? 'S' : 'T');
-    (status++)->rune = (buf->copy_indent ? 'I' : 'i');
-    (status++)->rune = (SearchDir < 0 ? '<' : '>');
-    (status++)->rune = (buf->modified ? '*' : ' ');
-    (status++)->rune = ' ';
-    char* path = (buf->path ? buf->path : "*scratch*");
-    size_t len = strlen(path);
-    if (len > ncols-4) {
-        (status++)->rune = '.';
-        (status++)->rune = '.';
-        (status++)->rune = '.';
-        path += (len - ncols) + 6;
-    }
     while (*path)
         (status++)->rune = *path++;
     draw_runes(2, 2, fg, 0, glyphs, status - glyphs);
@@ -418,7 +405,7 @@ static void redraw(int width, int height) {
     layout(width, height);
     x11_draw_rect(CLR_BASE03, 0, 0, width, height);
     x11_draw_rect(CLR_BASE02, 0, Regions[EDIT].y-2, fwidth + 4, height - Regions[EDIT].y + 2);
-    
+
     draw_status(CLR_BASE1, (width - 4) / x11_font_width(Font));
     draw_region(TAGS);
     draw_region(EDIT);
@@ -435,19 +422,19 @@ static void delete(void) {
 
 static void del_to_bol(void) {
     view_bol(currview(), true);
-    if (view_selsize(currview()) > 0) 
+    if (view_selsize(currview()) > 0)
         delete();
 }
 
 static void del_to_eol(void) {
     view_eol(currview(), true);
-    if (view_selsize(currview()) > 0) 
+    if (view_selsize(currview()) > 0)
         delete();
 }
 
 static void del_to_bow(void) {
     view_byword(currview(), LEFT, true);
-    if (view_selsize(currview()) > 0) 
+    if (view_selsize(currview()) > 0)
         delete();
 }
 
@@ -606,10 +593,10 @@ static void cmd_exec(char* cmd) {
         if (op != '<') dest = Focused;
         output = cmdread(ShellCmd, &error);
     }
-    
+
     if (error)
         view_append(getview(TAGS), chomp(error));
-    
+
     if (output) {
         if (op == '>')
             view_append(getview(dest), chomp(output));
diff --git a/xedit.c b/xedit.c
index 1d2b0a0e115760651884064c0353f8351e740e62..b8d57696e40f84544d6c2e9cb73d48bc010d2454 100644 (file)
--- a/xedit.c
+++ b/xedit.c
 #include <utf.h>
 #include <edit.h>
 #include <ctype.h>
+#include <win.h>
 
 #ifdef TEST
 #define exit mockexit
 #endif
 
-enum RegionId {
-    STATUS   = 0,
-    TAGS     = 1,
-    EDIT     = 2,
-    NREGIONS = 3
-};
-
-// Input Handlers
-static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y);
-static void key_handler(int mods, Rune key);
-
-// Drawing Routines
-static void draw_runes(size_t x, size_t y, int fg, int bg, UGlyph* glyphs, size_t rlen);
-static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols);
-static void draw_status(int fg, size_t ncols);
-static void draw_region(enum RegionId id);
-static void layout(int width, int height);
-static void redraw(int width, int height);
-
-// UI Callbacks
-static void delete(void);
-static void del_to_bol(void);
-static void del_to_eol(void);
-static void del_to_bow(void);
-static void backspace(void);
-static void cursor_bol(void);
-static void cursor_eol(void);
-static void cursor_home(void);
-static void cursor_end(void);
-static void cursor_up(void);
-static void cursor_dn(void);
-static void cursor_left(void);
-static void cursor_right(void);
-static void page_up(void);
-static void page_dn(void);
-static void select_prev(void);
-static void change_focus(void);
-static void quit(void);
-static void save(void);
-static void undo(void);
-static void redo(void);
-static void tag_undo(void);
-static void tag_redo(void);
-static void cut(void);
-static void copy(void);
-static void paste(void);
-static void search(void);
-static void execute(void);
-static void find(char* arg);
-static void open_file(void);
-static void pick_ctag(void);
-static void goto_ctag(void);
-static void tabs(void);
-static void indent(void);
-static void del_indent(void);
-static void add_indent(void);
-static void eol_mode(void);
-static void debug_dump(void);
-static void new_win(void);
-
-// Tag/Cmd Execution
-static Tag* tag_lookup(char* cmd);
-static void tag_exec(Tag* tag, char* arg);
-static void cmd_exec(char* cmd);
-static void exec(char* cmd);
-
-// Mouse Handling
-static void mouse_left(enum RegionId id, size_t count, size_t row, size_t col);
-static void mouse_middle(enum RegionId id, size_t count, size_t row, size_t col);
-static void mouse_right(enum RegionId id, size_t count, size_t row, size_t col);
-static void mouse_wheelup(enum RegionId id, size_t count, size_t row, size_t col);
-static void mouse_wheeldn(enum RegionId id, size_t count, size_t row, size_t col);
-
-// Region Utils
-static View* getview(enum RegionId id);
-static Buf* getbuf(enum RegionId id);
-static View* currview(void);
-static Buf* currbuf(void);
-static enum RegionId getregion(size_t x, size_t y);
-static Sel* getsel(enum RegionId id);
-static Sel* currsel(void);
-
-/* Global Data
- *****************************************************************************/
-static int SearchDir = DOWN;
-static enum RegionId Focused = EDIT;
-static Region Regions[NREGIONS] = { 0 };
-static ButtonState MouseBtns[MOUSE_BTN_COUNT] = { 0 };
-static XFont Font;
-static XConfig Config = {
-    .redraw       = redraw,
-    .handle_key   = key_handler,
-    .handle_mouse = mouse_handler,
-    .shutdown     = quit,
-    .palette      = COLOR_PALETTE
-};
-
-Tag Builtins[] = {
-    { .tag = "Quit",   .action.noarg = quit     },
-    { .tag = "Save",   .action.noarg = save     },
-    { .tag = "Cut",    .action.noarg = cut      },
-    { .tag = "Copy",   .action.noarg = copy     },
-    { .tag = "Paste",  .action.noarg = paste    },
-    { .tag = "Undo",   .action.noarg = tag_undo },
-    { .tag = "Redo",   .action.noarg = tag_redo },
-    { .tag = "Find",   .action.arg   = find     },
-    { .tag = "Tabs",   .action.noarg = tabs     },
-    { .tag = "Indent", .action.noarg = indent   },
-    { .tag = "Eol",    .action.noarg = eol_mode },
-    { .tag = NULL,     .action.noarg = NULL     }
-};
-
-void (*MouseActs[MOUSE_BTN_COUNT])(enum RegionId id, size_t count, size_t row, size_t col) = {
-    [MOUSE_BTN_LEFT]      = mouse_left,
-    [MOUSE_BTN_MIDDLE]    = mouse_middle,
-    [MOUSE_BTN_RIGHT]     = mouse_right,
-    [MOUSE_BTN_WHEELUP]   = mouse_wheelup,
-    [MOUSE_BTN_WHEELDOWN] = mouse_wheeldn,
-};
-
-static KeyBinding Bindings[] = {
-    /* Cursor Movements */
-    { ModAny, KEY_HOME,  cursor_home  },
-    { ModAny, KEY_END,   cursor_end   },
-    { ModAny, KEY_UP,    cursor_up    },
-    { ModAny, KEY_DOWN,  cursor_dn    },
-    { ModAny, KEY_LEFT,  cursor_left  },
-    { ModAny, KEY_RIGHT, cursor_right },
-    
-    /* Standard Unix Shortcuts */
-    { ModCtrl, 'u', del_to_bol  },
-    { ModCtrl, 'k', del_to_eol  },
-    { ModCtrl, 'w', del_to_bow  },
-    { ModCtrl, 'h', backspace   },
-    { ModCtrl, 'a', cursor_bol  },
-    { ModCtrl, 'e', cursor_eol  },
-
-    /* Standard Text Editing Shortcuts */
-    { ModCtrl, 's', save  },
-    { ModCtrl, 'z', undo  },
-    { ModCtrl, 'y', redo  },
-    { ModCtrl, 'x', cut   },
-    { ModCtrl, 'c', copy  },
-    { ModCtrl, 'v', paste },
-    
-    /* Block Indent */
-    { ModCtrl, '[', del_indent },
-    { ModCtrl, ']', add_indent },
-
-    /* Common Special Keys */
-    { ModNone, KEY_PGUP,      page_up   },
-    { ModNone, KEY_PGDN,      page_dn   },
-    { ModAny,  KEY_DELETE,    delete    },
-    { ModAny,  KEY_BACKSPACE, backspace },
-
-    /* Implementation Specific */
-    { ModNone,          KEY_ESCAPE, select_prev  },
-    { ModCtrl,          KEY_ESCAPE, debug_dump   },
-    { ModCtrl,          't',        change_focus },
-    { ModCtrl,          'q',        quit         },
-    { ModCtrl,          'f',        search       },
-    { ModCtrl|ModShift, 'f',        search       },
-    { ModCtrl,          'd',        execute      },
-    { ModCtrl,          'o',        open_file    },
-    { ModCtrl,          'p',        pick_ctag    },
-    { ModCtrl,          'g',        goto_ctag    },
-    { ModCtrl,          'n',        new_win      },
-};
-
-/* External Commands
- *****************************************************************************/
 /* The shell: Filled in with $SHELL. Used to execute commands */
 static char* ShellCmd[] = { NULL, "-c", NULL, NULL };
+static char* SedCmd[] = { "sed", "-e", NULL, NULL };
 static char* PickFileCmd[] = { "xfilepick", ".", NULL };
 static char* PickTagCmd[] = { "xtagpick", "tags", NULL, NULL };
 static char* OpenCmd[] = { "xedit", NULL, NULL };
-static char* SedCmd[] = { "sed", "-e", NULL, NULL };
+static int SearchDir = DOWN;
+static Tag Builtins[];
 
-/* Main Routine
+/* Tag/Cmd Execution
  *****************************************************************************/
-#ifndef TEST
-int main(int argc, char** argv) {
-    /* setup the shell */
-    ShellCmd[0] = getenv("SHELL");
-    if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh";
-    /* load the buffer views */
-    char* tags = getenv("EDITTAGS");
-    view_init(getview(TAGS), NULL);
-    view_putstr(getview(TAGS), (tags ? tags : DEFAULT_TAGS));
-    view_selprev(getview(TAGS)); // clear the selection
-    buf_logclear(getbuf(TAGS));
-    view_init(getview(EDIT), (argc > 1 ? argv[1] : NULL));
-    /* initialize the display engine */
-    x11_init(&Config);
-    x11_window("edit", Width, Height);
-    x11_show();
-    Font = x11_font_load(FONTNAME);
-    x11_loop();
-    return 0;
+static Tag* tag_lookup(char* cmd) {
+    size_t len = 0;
+    Tag* tags = Builtins;
+    for (char* tag = cmd; *tag && !isspace(*tag); tag++, len++);
+    while (tags->tag) {
+        if (!strncmp(tags->tag, cmd, len))
+            return tags;
+        tags++;
+    }
+    return NULL;
 }
-#endif
 
-static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y) {
-    enum RegionId id = getregion(x, y);
-    if (id != TAGS && id != EDIT) return;
-    if (Focused != id) Focused = id;
-    size_t row = (y-Regions[id].y) / x11_font_height(Font);
-    size_t col = (x-Regions[id].x) / x11_font_width(Font);
-    if (act == MOUSE_ACT_MOVE) {
-        if (MouseBtns[MOUSE_BTN_LEFT].pressed) {
-            if (MouseBtns[MOUSE_BTN_LEFT].count == 1) {
-                view_setcursor(getview(id), row, col);
-                MouseBtns[MOUSE_BTN_LEFT].count = 0;
-            } else {
-                view_selext(getview(id), row, col);
-            }
-        }
+static void tag_exec(Tag* tag, char* arg) {
+    /* if we didnt get an arg, find one in the selection */
+    if (!arg) arg = view_getstr(win_view(TAGS), NULL);
+    if (!arg) arg = view_getstr(win_view(EDIT), NULL);
+    /* execute the tag handler */
+    tag->action.arg(arg);
+    free(arg);
+}
+
+static void cmd_exec(char* cmd) {
+    char op = '\0';
+    if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>')
+        op = *cmd, cmd++;
+    ShellCmd[2] = cmd;
+    /* execute the command */
+    char *input = NULL, *output = NULL, *error = NULL;
+    WinRegion dest = EDIT;
+    if (op && op != '<' && op != '!' && 0 == view_selsize(win_view(EDIT)))
+        win_view(EDIT)->selection = (Sel){ .beg = 0, .end = buf_end(win_buf(EDIT)) };
+    input = view_getstr(win_view(EDIT), NULL);
+
+    if (op == '!') {
+        cmdrun(ShellCmd, NULL);
+    } else if (op == '>') {
+        dest = TAGS;
+        output = cmdwriteread(ShellCmd, input, &error);
+    } else if (op == '|') {
+        output = cmdwriteread(ShellCmd, input, &error);
+    } else if (op == ':') {
+        SedCmd[2] = cmd;
+        output = cmdwriteread(SedCmd, input, &error);
     } else {
-        MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN);
-        if (MouseBtns[btn].pressed) {
-            /* update the number of clicks and click time */
-            uint32_t now = getmillis();
-            uint32_t elapsed = now - MouseBtns[btn].time;
-            MouseBtns[btn].time = now;
-            MouseBtns[btn].region = id;
-            if (elapsed <= 250)
-                MouseBtns[btn].count++;
-            else
-                MouseBtns[btn].count = 1;
-        } else if (MouseBtns[btn].count > 0) {
-            /* execute the action on button release */
-            if (MouseActs[btn])
-                MouseActs[btn](id, MouseBtns[btn].count, row, col);
-        }
+        if (op != '<') dest = win_getregion();
+        output = cmdread(ShellCmd, &error);
     }
+    
+    if (error)
+        view_append(win_view(TAGS), chomp(error));
+    
+    if (output) {
+        if (op == '>')
+            view_append(win_view(dest), chomp(output));
+        else
+            view_putstr(win_view(dest), output);
+        win_setregion(dest);
+    }
+    /* cleanup */
+    free(input);
+    free(output);
+    free(error);
 }
 
-static void key_handler(int mods, Rune key) {
-    /* handle the proper line endings */
-    if (key == '\r') key = '\n';
-    if (key == '\n' && currview()->buffer.crlf) key = RUNE_CRLF;
-    /* search for a key binding entry */
-    uint32_t mkey = tolower(key);
-    for (int i = 0; i < nelem(Bindings); i++) {
-        int keymods = Bindings[i].mods;
-        if ((mkey == Bindings[i].key) && (keymods == ModAny || keymods == mods)) {
-            Bindings[i].action();
-            return;
-        }
+static void exec(char* cmd) {
+    /* skip leading space */
+    for (; *cmd && isspace(*cmd); cmd++);
+    if (!*cmd) return;
+    /* see if it matches a builtin tag */
+    Tag* tag = tag_lookup(cmd);
+    if (tag) {
+        while (*cmd && !isspace(*cmd++));
+        tag_exec(tag, (*cmd ? stringdup(cmd) : NULL));
+    } else {
+        cmd_exec(cmd);
     }
-    /* fallback to just inserting the rune if it doesn't fall in the private use area.
-     * the private use area is used to encode special keys */
-    if (key < 0xE000 || key > 0xF8FF)
-        view_insert(currview(), true, key);
 }
 
-/* Drawing Routines
+/* Action Callbacks
  *****************************************************************************/
-#ifndef TEST
-static void draw_runes(size_t x, size_t y, int fg, int bg, UGlyph* glyphs, size_t rlen) {
-    XGlyphSpec specs[rlen];
-    while (rlen) {
-        size_t nspecs = x11_font_getglyphs(specs, (XGlyph*)glyphs, rlen, Font, x, y);
-        x11_draw_glyphs(fg, bg, specs, nspecs);
-        rlen -= nspecs;
-    }
+static void delete(void) {
+    bool byword = x11_keymodsset(ModCtrl);
+    view_delete(win_view(FOCUSED), RIGHT, byword);
 }
 
-static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols) {
-    XGlyphSpec specs[rlen];
-    size_t i = 0;
-    while (rlen && i < ncols) {
-        int numspecs = 0;
-        uint32_t attr = glyphs[i].attr;
-        while (i < ncols && glyphs[i].attr == attr) {
-            x11_font_getglyph(Font, &(specs[numspecs]), glyphs[i].rune);
-            specs[numspecs].x = x;
-            specs[numspecs].y = y - x11_font_descent(Font);
-            x += x11_font_width(Font);
-            numspecs++;
-            i++;
-            /* skip over null chars which mark multi column runes */
-            for (; i < ncols && !glyphs[i].rune; i++)
-                x += x11_font_width(Font);
-        }
-        /* Draw the glyphs with the proper colors */
-        uint8_t bg = attr >> 8;
-        uint8_t fg = attr & 0xFF;
-        x11_draw_glyphs(fg, bg, specs, numspecs);
-        rlen -= numspecs;
-    }
+static void onpaste(char* text) {
+    view_putstr(win_view(FOCUSED), text);
 }
 
-static void draw_status(int fg, size_t ncols) {
-    Buf* buf = getbuf(EDIT);
-    UGlyph glyphs[ncols], *status = glyphs;
-    (status++)->rune = (buf->charset == BINARY ? 'B' : 'U');
-    (status++)->rune = (buf->crlf ? 'C' : 'N');
-    (status++)->rune = (buf->expand_tabs ? 'S' : 'T');
-    (status++)->rune = (buf->copy_indent ? 'I' : 'i');
-    (status++)->rune = (SearchDir < 0 ? '<' : '>');
-    (status++)->rune = (buf->modified ? '*' : ' ');
-    (status++)->rune = ' ';
-    char* path = (buf->path ? buf->path : "*scratch*");
-    size_t len = strlen(path);
-    if (len > ncols-4) {
-        (status++)->rune = '.';
-        (status++)->rune = '.';
-        (status++)->rune = '.';
-        path += (len - ncols) + 6;
-    }
-    while (*path)
-        (status++)->rune = *path++;
-    draw_runes(2, 2, fg, 0, glyphs, status - glyphs);
-}
-
-static void draw_region(enum RegionId id) {
-    size_t fheight = x11_font_height(Font);
-    size_t fwidth  = x11_font_width(Font);
-    /* update the screen buffer and retrieve cursor coordinates */
-    View* view = getview(id);
-    size_t csrx = SIZE_MAX, csry = SIZE_MAX;
-    view_update(view, &csrx, &csry);
-    /* draw the region to the frame buffer */
-    if (id == TAGS)
-        x11_draw_rect(CLR_BASE02, Regions[id].x-2, Regions[id].y-2, Regions[id].width+4, Regions[id].height+4);
-    x11_draw_rect(CLR_BASE01, 0, Regions[id].y - 3, Regions[id].width + 4, 1);
-    for (size_t y = 0; y < view->nrows; y++) {
-        Row* row = view_getrow(view, y);
-        draw_glyphs(2, Regions[id].y + ((y+1) * fheight), row->cols, row->rlen, row->len);
+static void cut(void) {
+    char* str = view_getstr(win_view(FOCUSED), NULL);
+    x11_setsel(CLIPBOARD, str);
+    if (str && *str) delete();
+}
+
+static void paste(void) {
+    assert(x11_getsel(CLIPBOARD, onpaste));
+}
+
+static void copy(void) {
+    char* str = view_getstr(win_view(FOCUSED), NULL);
+    x11_setsel(CLIPBOARD, str);
+}
+
+static void quit(void) {
+    static uint64_t before = 0;
+    uint64_t now = getmillis();
+    if (!win_buf(EDIT)->modified || (now-before) <= 250) {
+        x11_deinit();
+    } else {
+        view_append(win_view(TAGS),
+            "File is modified. Repeat action twice in < 250ms to quit.");
     }
-    /* Place cursor on screen */
-    if (id == Focused && csrx != SIZE_MAX && csry != SIZE_MAX)
-        x11_draw_rect(CLR_BASE3, 2 + csrx * fwidth, Regions[id].y + (csry * fheight), 1, fheight);
-
-    if (Regions[id].warp_ptr) {
-        Regions[id].warp_ptr = false;
-        size_t x = 2 + (csrx * fwidth) - (fwidth/2);
-        size_t y = Regions[id].y + (csry * fheight) + (fheight/2);
-        x11_warp_mouse(x,y);
+    before = now;
+}
+
+static void save(void) {
+    buf_save(win_buf(EDIT));
+}
+
+/* Mouse Handling
+ *****************************************************************************/
+void mouse_left(WinRegion id, size_t count, size_t row, size_t col) {
+    if (count == 1) {
+        if (x11_keymodsset(ModShift))
+            view_selext(win_view(id), row, col);
+        else
+            view_setcursor(win_view(id), row, col);
+    } else if (count == 2) {
+        view_select(win_view(id), row, col);
+    } else if (count == 3) {
+        view_selword(win_view(id), row, col);
     }
 }
 
-static void layout(int width, int height) {
-    /* initialize all of the regions to overlap the status region */
-    size_t fheight = x11_font_height(Font);
-    size_t fwidth  = x11_font_width(Font);
-    for (int i = 0; i < NREGIONS; i++) {
-        Regions[i].x      = 2;
-        Regions[i].y      = 2;
-        Regions[i].width  = (width - 4);
-        Regions[i].height = fheight;
+void mouse_middle(WinRegion id, size_t count, size_t row, size_t col) {
+    if (win_btnpressed(MOUSE_BTN_LEFT)) {
+        cut();
+    } else {
+        char* str = view_fetchcmd(win_view(id), row, col);
+        if (str) exec(str);
+        free(str);
     }
-    /* Place the tag region relative to status */
-    Regions[TAGS].y = 5 + Regions[STATUS].y + Regions[STATUS].height;
-    size_t maxtagrows = ((height - Regions[TAGS].y - 5) / 4) / fheight;
-    size_t tagcols    = Regions[TAGS].width / fwidth;
-    size_t tagrows    = view_limitrows(getview(TAGS), maxtagrows, tagcols);
-    Regions[TAGS].height = tagrows * fheight;
-    view_resize(getview(TAGS), tagrows, tagcols);
-    /* Place the edit region relative to status */
-    Regions[EDIT].y      = 5 + Regions[TAGS].y + Regions[TAGS].height;
-    Regions[EDIT].height = (height - Regions[EDIT].y - 5);
-    view_resize(getview(EDIT), Regions[EDIT].height / fheight, Regions[EDIT].width / fwidth);
-}
-
-static void redraw(int width, int height) {
-    size_t fheight = x11_font_height(Font);
-    size_t fwidth  = x11_font_width(Font);
-    /* if the window is too small, don't bother updating. */
-    if (width < fwidth || height < (4 * fheight))
-        return;
-    layout(width, height);
-    x11_draw_rect(CLR_BASE03, 0, 0, width, height);
-    x11_draw_rect(CLR_BASE02, (79 * fwidth) + 2, Regions[EDIT].y-2, fwidth, height - Regions[EDIT].y + 2);
-    draw_status(CLR_BASE1, (width - 4) / x11_font_width(Font));
-    draw_region(TAGS);
-    draw_region(EDIT);
-    cmdreap(); // cleanup any zombie child processes
 }
-#endif
 
-/* UI Callbacks
- *****************************************************************************/
-static void delete(void) {
-    bool byword = x11_keymodsset(ModCtrl);
-    view_delete(currview(), RIGHT, byword);
+void mouse_right(WinRegion id, size_t count, size_t row, size_t col) {
+    if (win_btnpressed(MOUSE_BTN_LEFT)) {
+        paste();
+    } else {
+        SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
+        view_find(win_view(id), SearchDir, row, col);
+        //Regions[id].warp_ptr = true;
+    }
 }
 
+/* Keyboard Handling */
 static void del_to_bol(void) {
-    view_bol(currview(), true);
-    if (view_selsize(currview()) > 0) 
+    view_bol(win_view(FOCUSED), true);
+    if (view_selsize(win_view(FOCUSED)) > 0) 
         delete();
 }
 
 static void del_to_eol(void) {
-    view_eol(currview(), true);
-    if (view_selsize(currview()) > 0) 
+    view_eol(win_view(FOCUSED), true);
+    if (view_selsize(win_view(FOCUSED)) > 0) 
         delete();
 }
 
 static void del_to_bow(void) {
-    view_byword(currview(), LEFT, true);
-    if (view_selsize(currview()) > 0) 
+    view_byword(win_view(FOCUSED), LEFT, true);
+    if (view_selsize(win_view(FOCUSED)) > 0) 
         delete();
 }
 
 static void backspace(void) {
     bool byword = x11_keymodsset(ModCtrl);
-    view_delete(currview(), LEFT, byword);
+    view_delete(win_view(FOCUSED), LEFT, byword);
 }
 
 static void cursor_bol(void) {
-    view_bol(currview(), false);
+    view_bol(win_view(FOCUSED), false);
 }
 
 static void cursor_eol(void) {
-    view_eol(currview(), false);
+    view_eol(win_view(FOCUSED), false);
 }
 
 static void cursor_home(void) {
     bool extsel = x11_keymodsset(ModShift);
     if (x11_keymodsset(ModCtrl))
-        view_bof(currview(), extsel);
+        view_bof(win_view(FOCUSED), extsel);
     else
-        view_bol(currview(), extsel);
+        view_bol(win_view(FOCUSED), extsel);
 }
 
 static void cursor_end(void) {
     bool extsel = x11_keymodsset(ModShift);
     if (x11_keymodsset(ModCtrl))
-        view_eof(currview(), extsel);
+        view_eof(win_view(FOCUSED), extsel);
     else
-        view_eol(currview(), extsel);
+        view_eol(win_view(FOCUSED), extsel);
 }
 
 static void cursor_up(void) {
     bool extsel = x11_keymodsset(ModShift);
-    view_byline(currview(), UP, extsel);
+    view_byline(win_view(FOCUSED), UP, extsel);
 }
 
 static void cursor_dn(void) {
     bool extsel = x11_keymodsset(ModShift);
-    view_byline(currview(), DOWN, extsel);
+    view_byline(win_view(FOCUSED), DOWN, extsel);
 }
 
 static void cursor_left(void) {
     bool extsel = x11_keymodsset(ModShift);
     if (x11_keymodsset(ModCtrl))
-        view_byword(currview(), LEFT, extsel);
+        view_byword(win_view(FOCUSED), LEFT, extsel);
     else
-        view_byrune(currview(), LEFT, extsel);
+        view_byrune(win_view(FOCUSED), LEFT, extsel);
 }
 
 static void cursor_right(void) {
     bool extsel = x11_keymodsset(ModShift);
     if (x11_keymodsset(ModCtrl))
-        view_byword(currview(), RIGHT, extsel);
+        view_byword(win_view(FOCUSED), RIGHT, extsel);
     else
-        view_byrune(currview(), RIGHT, extsel);
+        view_byrune(win_view(FOCUSED), RIGHT, extsel);
 }
 
 static void page_up(void) {
-    view_scrollpage(currview(), UP);
+    view_scrollpage(win_view(FOCUSED), UP);
 }
 
 static void page_dn(void) {
-    view_scrollpage(currview(), DOWN);
+    view_scrollpage(win_view(FOCUSED), DOWN);
 }
 
 static void select_prev(void) {
-    view_selprev(currview());
+    view_selprev(win_view(FOCUSED));
 }
 
 static void change_focus(void) {
-    Focused = (Focused == TAGS ? EDIT : TAGS);
-}
-
-static void quit(void) {
-    static uint64_t before = 0;
-    uint64_t now = getmillis();
-    if (!getbuf(EDIT)->modified || (now-before) <= 250) {
-        x11_deinit();
-    } else {
-        view_append(getview(TAGS),
-            "File is modified. Repeat action twice in < 250ms to quit.");
-    }
-    before = now;
-}
-
-static void save(void) {
-    buf_save(getbuf(EDIT));
+    win_setregion(win_getregion() == TAGS ? EDIT : TAGS);
 }
 
 static void undo(void) {
-    view_undo(currview());
+    view_undo(win_view(FOCUSED));
 }
 
 static void redo(void) {
-    view_redo(currview());
+    view_redo(win_view(FOCUSED));
 }
 
 static void tag_undo(void) {
-    view_undo(getview(EDIT));
+    view_undo(win_view(EDIT));
 }
 
 static void tag_redo(void) {
-    view_redo(getview(EDIT));
-}
-
-static void cut(void) {
-    char* str = view_getstr(currview(), NULL);
-    x11_setsel(CLIPBOARD, str);
-    if (str && *str) delete();
-}
-
-static void copy(void) {
-    char* str = view_getstr(currview(), NULL);
-    x11_setsel(CLIPBOARD, str);
-}
-
-static void onpaste(char* text) {
-    view_putstr(currview(), text);
-}
-
-static void paste(void) {
-    assert(x11_getsel(CLIPBOARD, onpaste));
+    view_redo(win_view(EDIT));
 }
 
 static void search(void) {
     SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
-    char* str = view_getctx(currview());
-    view_findstr(getview(EDIT), SearchDir, str);
+    char* str = view_getctx(win_view(FOCUSED));
+    view_findstr(win_view(EDIT), SearchDir, str);
     free(str);
-    Regions[EDIT].warp_ptr = true;
+    //Regions[EDIT].warp_ptr = true;
 }
 
 static void execute(void) {
-    char* str = view_getcmd(currview());
+    char* str = view_getcmd(win_view(FOCUSED));
     if (str) exec(str);
     free(str);
 }
 
 static void find(char* arg) {
     SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
-    view_findstr(getview(EDIT), SearchDir, arg);
+    view_findstr(win_view(EDIT), SearchDir, arg);
 }
 
 static void open_file(void) {
     char* file = cmdread(PickFileCmd, NULL);
     if (file) {
         file = chomp(file);
-        if (!getbuf(EDIT)->path && !getbuf(EDIT)->modified) {
-            buf_load(getbuf(EDIT), file);
+        if (!win_buf(EDIT)->path && !win_buf(EDIT)->modified) {
+            buf_load(win_buf(EDIT), file);
         } else {
             OpenCmd[1] = file;
             cmdrun(OpenCmd, NULL);
@@ -574,13 +318,13 @@ static void pick_symbol(char* symbol) {
     PickTagCmd[2] = symbol;
     char* pick = cmdread(PickTagCmd, NULL);
     if (pick) {
-        Buf* buf = getbuf(EDIT);
+        Buf* buf = win_buf(EDIT);
         if (buf->path && 0 == strncmp(buf->path, pick, strlen(buf->path))) {
-            view_setln(getview(EDIT), strtoul(strrchr(pick, ':')+1, NULL, 0));
-            Focused = EDIT;
+            view_setln(win_view(EDIT), strtoul(strrchr(pick, ':')+1, NULL, 0));
+            win_setregion(EDIT);
         } else {
             if (!buf->path && !buf->modified) {
-                view_init(getview(EDIT), pick);
+                view_init(win_view(EDIT), pick);
             } else {
                 OpenCmd[1] = chomp(pick);
                 cmdrun(OpenCmd, NULL);
@@ -594,12 +338,12 @@ static void pick_ctag(void) {
 }
 
 static void goto_ctag(void) {
-    char* str = view_getctx(currview());
+    char* str = view_getctx(win_view(FOCUSED));
     if (str) {
         size_t line = strtoul(str, NULL, 0);
         if (line) {
-            view_setln(getview(EDIT), line);
-            Focused = EDIT;
+            view_setln(win_view(EDIT), line);
+            win_setregion(EDIT);
         } else {
             pick_symbol(str);
         }
@@ -608,224 +352,141 @@ static void goto_ctag(void) {
 }
 
 static void tabs(void) {
-    bool enabled = !(getbuf(EDIT)->expand_tabs);
-    getbuf(EDIT)->expand_tabs = enabled;
-    getbuf(TAGS)->expand_tabs = enabled;
+    bool enabled = !(win_buf(EDIT)->expand_tabs);
+    win_buf(EDIT)->expand_tabs = enabled;
+    win_buf(TAGS)->expand_tabs = enabled;
 }
 
 static void indent(void) {
-    bool enabled = !(getbuf(EDIT)->copy_indent);
-    getbuf(EDIT)->copy_indent = enabled;
-    getbuf(TAGS)->copy_indent = enabled;
+    bool enabled = !(win_buf(EDIT)->copy_indent);
+    win_buf(EDIT)->copy_indent = enabled;
+    win_buf(TAGS)->copy_indent = enabled;
 }
 
 static void del_indent(void) {
-    view_indent(currview(), LEFT);
+    view_indent(win_view(FOCUSED), LEFT);
 }
 
 static void add_indent(void) {
-    view_indent(currview(), RIGHT);
+    view_indent(win_view(FOCUSED), RIGHT);
 }
 
 static void eol_mode(void) {
-    int crlf = getbuf(EDIT)->crlf;
-    getbuf(EDIT)->crlf = !crlf;
-    getbuf(TAGS)->crlf = !crlf;
+    int crlf = win_buf(EDIT)->crlf;
+    win_buf(EDIT)->crlf = !crlf;
+    win_buf(TAGS)->crlf = !crlf;
     exec(crlf ? "|dos2unix" : "|unix2dos");
 }
 
-static void dump_log(Log* log) {
-    for (; log != NULL; log = log->next) {
-        if (log->insert) {
-            printf("    INS %d %lu %lu\n", 
-                log->transid, log->data.ins.beg, log->data.ins.end);
-        } else {
-            printf("    DEL %d %lu %lu\n",
-                log->transid, log->data.del.off, log->data.del.len);
-        }
-    }
-}
-
-static void debug_dump(void) {
-    Buf* buf = currbuf();
-    Log* log;
-    printf("path:     '%s'\n", buf->path);
-    printf("charset:  %d\n", buf->charset);
-    printf("crlf:     %d\n", buf->crlf);
-    printf("bufsize:  %lu\n", buf->bufsize);
-    printf("modified: %d\n", buf->modified);
-    printf("tab_mode: %d\n", buf->expand_tabs);
-    printf("indent:   %d\n", buf->copy_indent);
-    printf("bufstart: %p\n", (void*)buf->bufstart);
-    printf("bufend:   %p\n", (void*)buf->bufend);
-    printf("gapstart: %p\n", (void*)buf->gapstart);
-    printf("gapend:   %p\n", (void*)buf->gapend);
-    printf("undo:\n");
-    dump_log(buf->undo);
-    printf("redo:\n");
-    dump_log(buf->redo);
-}
-
 static void new_win(void) {
     cmd_exec("!edit");
 }
 
-/* Tag/Cmd Execution
+/* Main Routine
  *****************************************************************************/
-static Tag* tag_lookup(char* cmd) {
-    size_t len = 0;
-    Tag* tags = Builtins;
-    for (char* tag = cmd; *tag && !isspace(*tag); tag++, len++);
-    while (tags->tag) {
-        if (!strncmp(tags->tag, cmd, len))
-            return tags;
-        tags++;
-    }
-    return NULL;
-}
-
-static void tag_exec(Tag* tag, char* arg) {
-    /* if we didnt get an arg, find one in the selection */
-    if (!arg) arg = view_getstr(getview(TAGS), NULL);
-    if (!arg) arg = view_getstr(getview(EDIT), NULL);
-    /* execute the tag handler */
-    tag->action.arg(arg);
-    free(arg);
-}
-
-static void cmd_exec(char* cmd) {
-    char op = '\0';
-    if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>')
-        op = *cmd, cmd++;
-    ShellCmd[2] = cmd;
-    /* execute the command */
-    char *input = NULL, *output = NULL, *error = NULL;
-    enum RegionId dest = EDIT;
-    if (op && op != '<' && op != '!' && 0 == view_selsize(getview(EDIT)))
-        getview(EDIT)->selection = (Sel){ .beg = 0, .end = buf_end(getbuf(EDIT)) };
-    input = view_getstr(getview(EDIT), NULL);
+static Tag Builtins[] = {
+    { .tag = "Quit",   .action.noarg = quit     },
+    { .tag = "Save",   .action.noarg = save     },
+    { .tag = "Cut",    .action.noarg = cut      },
+    { .tag = "Copy",   .action.noarg = copy     },
+    { .tag = "Paste",  .action.noarg = paste    },
+    { .tag = "Undo",   .action.noarg = tag_undo },
+    { .tag = "Redo",   .action.noarg = tag_redo },
+    { .tag = "Find",   .action.arg   = find     },
+    { .tag = "Tabs",   .action.noarg = tabs     },
+    { .tag = "Indent", .action.noarg = indent   },
+    { .tag = "Eol",    .action.noarg = eol_mode },
+    { .tag = NULL,     .action.noarg = NULL     }
+}; 
 
-    if (op == '!') {
-        cmdrun(ShellCmd, NULL);
-    } else if (op == '>') {
-        dest = TAGS;
-        output = cmdwriteread(ShellCmd, input, &error);
-    } else if (op == '|') {
-        output = cmdwriteread(ShellCmd, input, &error);
-    } else if (op == ':') {
-        SedCmd[2] = cmd;
-        output = cmdwriteread(SedCmd, input, &error);
-    } else {
-        if (op != '<') dest = Focused;
-        output = cmdread(ShellCmd, &error);
-    }
-    
-    if (error)
-        view_append(getview(TAGS), chomp(error));
+static KeyBinding Bindings[] = {
+    /* Cursor Movements */
+    { ModAny, KEY_HOME,  cursor_home  },
+    { ModAny, KEY_END,   cursor_end   },
+    { ModAny, KEY_UP,    cursor_up    },
+    { ModAny, KEY_DOWN,  cursor_dn    },
+    { ModAny, KEY_LEFT,  cursor_left  },
+    { ModAny, KEY_RIGHT, cursor_right },
     
-    if (output) {
-        if (op == '>')
-            view_append(getview(dest), chomp(output));
-        else
-            view_putstr(getview(dest), output);
-        Focused = dest;
-    }
-    /* cleanup */
-    free(input);
-    free(output);
-    free(error);
-}
-
-static void exec(char* cmd) {
-    /* skip leading space */
-    for (; *cmd && isspace(*cmd); cmd++);
-    if (!*cmd) return;
-    /* see if it matches a builtin tag */
-    Tag* tag = tag_lookup(cmd);
-    if (tag) {
-        while (*cmd && !isspace(*cmd++));
-        tag_exec(tag, (*cmd ? stringdup(cmd) : NULL));
-    } else {
-        cmd_exec(cmd);
-    }
-}
+    /* Standard Unix Shortcuts */
+    { ModCtrl, 'u', del_to_bol  },
+    { ModCtrl, 'k', del_to_eol  },
+    { ModCtrl, 'w', del_to_bow  },
+    { ModCtrl, 'h', backspace   },
+    { ModCtrl, 'a', cursor_bol  },
+    { ModCtrl, 'e', cursor_eol  },
 
-/* Mouse Handling
- *****************************************************************************/
-static void mouse_left(enum RegionId id, size_t count, size_t row, size_t col) {
-    if (count == 1) {
-        if (x11_keymodsset(ModShift))
-            view_selext(getview(id), row, col);
-        else
-            view_setcursor(getview(id), row, col);
-    } else if (count == 2) {
-        view_select(getview(id), row, col);
-    } else if (count == 3) {
-        view_selword(getview(id), row, col);
-    }
-}
+    /* Standard Text Editing Shortcuts */
+    { ModCtrl, 's', save  },
+    { ModCtrl, 'z', undo  },
+    { ModCtrl, 'y', redo  },
+    { ModCtrl, 'x', cut   },
+    { ModCtrl, 'c', copy  },
+    { ModCtrl, 'v', paste },
+    
+    /* Block Indent */
+    { ModCtrl, '[', del_indent },
+    { ModCtrl, ']', add_indent },
 
-static void mouse_middle(enum RegionId id, size_t count, size_t row, size_t col) {
-    if (MouseBtns[MOUSE_BTN_LEFT].pressed) {
-        cut();
-    } else {
-        char* str = view_fetchcmd(getview(id), row, col);
-        if (str) exec(str);
-        free(str);
-    }
-}
+    /* Common Special Keys */
+    { ModNone, KEY_PGUP,      page_up   },
+    { ModNone, KEY_PGDN,      page_dn   },
+    { ModAny,  KEY_DELETE,    delete    },
+    { ModAny,  KEY_BACKSPACE, backspace },
 
-static void mouse_right(enum RegionId id, size_t count, size_t row, size_t col) {
-    if (MouseBtns[MOUSE_BTN_LEFT].pressed) {
-        paste();
-    } else {
-        SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
-        view_find(getview(id), SearchDir, row, col);
-        Regions[id].warp_ptr = true;
-    }
-}
+    /* Implementation Specific */
+    { ModNone,          KEY_ESCAPE, select_prev  },
+    { ModCtrl,          't',        change_focus },
+    { ModCtrl,          'q',        quit         },
+    { ModCtrl,          'f',        search       },
+    { ModCtrl|ModShift, 'f',        search       },
+    { ModCtrl,          'd',        execute      },
+    { ModCtrl,          'o',        open_file    },
+    { ModCtrl,          'p',        pick_ctag    },
+    { ModCtrl,          'g',        goto_ctag    },
+    { ModCtrl,          'n',        new_win      },
+    { 0, 0, 0 }
+};
 
-static void mouse_wheelup(enum RegionId id, size_t count, size_t row, size_t col) {
-    view_scroll(getview(id), -ScrollLines);
+void onupdate(void) {
+    static char status_bytes[256];
+    memset(status_bytes, 0, sizeof(status_bytes));
+    char* status = status_bytes;
+    Buf* buf = win_buf(EDIT);
+    *(status++) = (buf->charset == BINARY ? 'B' : 'U');
+    *(status++) = (buf->crlf ? 'C' : 'N');
+    *(status++) = (buf->expand_tabs ? 'S' : 'T');
+    *(status++) = (buf->copy_indent ? 'I' : 'i');
+    *(status++) = (SearchDir < 0 ? '<' : '>');
+    *(status++) = (buf->modified ? '*' : ' ');
+    *(status++) = ' ';
+    char* path = (buf->path ? buf->path : "*scratch*");
+    size_t remlen = sizeof(status_bytes) - strlen(status_bytes) - 1;
+    strncat(status, path, remlen);
+    win_settext(STATUS, status_bytes);
 }
 
-static void mouse_wheeldn(enum RegionId id, size_t count, size_t row, size_t col) {
-    view_scroll(getview(id), +ScrollLines);
+#ifndef TEST
+int main(int argc, char** argv) {
+    /* setup the shell */
+    ShellCmd[0] = getenv("SHELL");
+    if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh";
+    /* Create the window and enter the event loop */
+    win_init("edit");
+    char* tags = getenv("EDITTAGS");
+    win_settext(TAGS, (tags ? tags : DEFAULT_TAGS));
+    view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL));
+    win_setkeys(Bindings);
+    //win_setmouse(&MouseHandlers);
+    win_loop();
+    return 0;
 }
+#endif
 
-/* Region Utils
- *****************************************************************************/
-static View* getview(enum RegionId id) {
-    return &(Regions[id].view);
-}
 
-static Buf* getbuf(enum RegionId id) {
-    return &(getview(id)->buffer);
-}
 
-static View* currview(void) {
-    return getview(Focused);
-}
 
-static Buf* currbuf(void) {
-    return getbuf(Focused);
-}
 
-static enum RegionId getregion(size_t x, size_t y) {
-    for (int i = 0; i < NREGIONS; i++) {
-        size_t startx = Regions[i].x, endx = startx + Regions[i].width;
-        size_t starty = Regions[i].y, endy = starty + Regions[i].height;
-        if ((startx <= x && x <= endx) && (starty <= y && y <= endy))
-            return (enum RegionId)i;
-    }
-    return NREGIONS;
-}
 
-static Sel* getsel(enum RegionId id) {
-    return &(getview(id)->selection);
-}
 
-static Sel* currsel(void) {
-    return getsel(Focused);
-}