]> git.mdlowis.com Git - projs/tide.git/commitdiff
refactor and reorg x11.c pruning
authorMichael D. Lowis <mike@mdlowis.com>
Fri, 13 Apr 2018 01:54:09 +0000 (21:54 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Fri, 13 Apr 2018 01:54:09 +0000 (21:54 -0400)
inc/win.h
lib/x11.c
tide.c

index 43c7226131f4c373f4af39a1bebaf792ae2b621a..932f4d6e320388bc2fee8287a04ed134a31a8555 100644 (file)
--- a/inc/win.h
+++ b/inc/win.h
@@ -130,16 +130,15 @@ typedef struct {
     int h, w;
 } drawcsr;
 
-bool x11_keymodsset(int mask);
-bool x11_sel_get(int selid, void(*cbfn)(char*));
-bool x11_sel_set(int selid, char* str);
-
 void win_init(char* title, KeyBinding* bindings);
 void win_save(char* path);
 void win_loop(void);
 void win_quit(void);
 View* win_view(WinRegion id);
 Buf* win_buf(WinRegion id);
+bool win_keymodsset(int mask);
+bool win_sel_get(int selid, void(*cbfn)(char*));
+bool win_sel_set(int selid, char* str);
 
 /* move these to x11.c when possible */
 extern int SearchDir;
index 09e3e8bf2ac93b8751b9eb495d6922f2f310c1fe..c5d4dd5e274203f173cdd3bcf177cedfc6615519 100644 (file)
--- a/lib/x11.c
+++ b/lib/x11.c
@@ -22,70 +22,14 @@ struct XFont {
     XftFont* match;
 };
 
-#define PRESSED(btn) \
-    ((KeyBtnState & (1 << (btn + 7))) == (1 << (btn + 7)))
-
-/******************************************************************************/
-
-static void die(const char* msg);
-
-static WinRegion getregion(size_t x, size_t y);
-static struct XSel* selfetch(Atom atom);
-static void xftcolor(XftColor* xc, int id);
-
-static void x11_window(char* name);
-static void x11_font_load(char* name);
-static size_t x11_font_height(void);
-static size_t x11_font_width(void);
-static size_t x11_font_descent(void);
-static void getglyph(XGlyphSpec* spec, uint32_t rune);
-static size_t getglyphs(XGlyphSpec* specs, const XGlyph* glyphs, int len, int x, int y);
-static void x11_draw_rect(int color, int x, int y, int width, int height);
-static void x11_draw_glyphs(int fg, int bg, XGlyphSpec* specs, size_t nspecs, bool eol);
-
-static uint32_t special_keys(uint32_t key);
-static uint32_t getkey(XEvent* e);
-static void mouse_click(int btn, bool pressed, int x, int y);
-static void mouse_left(WinRegion id, bool pressed, size_t row, size_t col);
-static void mouse_middle(WinRegion id, bool pressed, size_t row, size_t col);
-static void mouse_right(WinRegion id, bool pressed, size_t row, size_t col);
-
-static void draw_view(int i, size_t nrows, drawcsr* csr, int bg, int fg, int sel);
-static void draw_hrule(drawcsr* csr);
-static void draw_scroll(drawcsr* csr);
-static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols);
-
-static void xupdate(Job* job);
-static void xfocus(XEvent* e);
-static void xkeypress(XEvent* e);
-static void xbtnpress(XEvent* e);
-static void xbtnrelease(XEvent* e);
-static void xbtnmotion(XEvent* e);
-static void xselclear(XEvent* e);
-static void xselnotify(XEvent* e);
-static void xselrequest(XEvent* e);
-static void xpropnotify(XEvent* e);
-static void xclientmsg(XEvent* e);
-static void xresize(XEvent* e);
-static void xexpose(XEvent* e);
-
-static void (*EventHandlers[LASTEvent])(XEvent*) = {
-    [FocusIn] = xfocus,
-    [FocusOut] = xfocus,
-    [KeyPress] = xkeypress,
-    [ButtonPress] = xbtnpress,
-    [ButtonRelease] = xbtnrelease,
-    [MotionNotify] = xbtnmotion,
-    [SelectionClear] = xselclear,
-    [SelectionNotify] = xselnotify,
-    [SelectionRequest] = xselrequest,
-    [ClientMessage] = xclientmsg,
-    [ConfigureNotify] = xresize,
+struct XSel {
+    char* name;
+    Atom atom;
+    char* text;
+    void (*callback)(char*);
 };
 
-/******************************************************************************/
-
-static struct {
+struct XWin {
     Time now;
     Window root;
     Display* display;
@@ -102,99 +46,84 @@ static struct {
     XIC xic;
     XIM xim;
     GC gc;
-} X;
-static int KeyBtnState;
-static Atom SelTarget;
-static struct XSel {
-    char* name;
-    Atom atom;
-    char* text;
-    void (*callback)(char*);
-} Selections[] = {
-    { .name = "PRIMARY" },
-    { .name = "CLIPBOARD" },
 };
 
+/******************************************************************************/
+
+static struct XWin X;
+static int KeyBtnState;
+static Atom SelTarget;
 static XFont CurrFont;
 static WinRegion Focused = EDIT;
 static View Regions[NREGIONS];
 static Rune LastKey;
 static KeyBinding* Keys = NULL;
 static int Divider;
+static struct XSel Selections[] = {
+    { .name = "PRIMARY" },
+    { .name = "CLIPBOARD" },
+};
 
 int SearchDir = DOWN;
 char* SearchTerm = NULL;
 
 /******************************************************************************/
 
-void win_init(char* title, KeyBinding* bindings) {
-    Keys = bindings;
-    view_init(&Regions[TAGS], NULL);
-    view_init(&Regions[EDIT], NULL);
-    signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal
-    setlocale(LC_CTYPE, "");
-    XSetLocaleModifiers("");
-    /* open the X display and get basic attributes */
-    if (!(X.display = XOpenDisplay(0)))
-        die("could not open display");
-    X.root = DefaultRootWindow(X.display);
-    XWindowAttributes wa;
-    XGetWindowAttributes(X.display, X.root, &wa);
-    X.visual   = wa.visual;
-    X.colormap = wa.colormap;
-    X.screen   = DefaultScreen(X.display);
-    X.depth    = DefaultDepth(X.display, X.screen);
-    x11_font_load(FontString);
-    x11_window("tide");
-    /* initialize selection atoms */
-    for (int i = 0; i < (sizeof(Selections) / sizeof(Selections[0])); i++)
-        Selections[i].atom = XInternAtom(X.display, Selections[i].name, 0);
-    SelTarget = XInternAtom(X.display, "UTF8_STRING", 0);
-    if (SelTarget == None)
-        SelTarget = XInternAtom(X.display, "STRING", 0);
-    /* Populate the  tags region */
-    View* view = win_view(TAGS);
-    view_putstr(view, TagString);
-    view_selprev(view); // clear the selection
-    buf_logclear(&(view->buffer));
-}
+#define PRESSED(btn) \
+    ((KeyBtnState & (1 << (btn + 7))) == (1 << (btn + 7)))
 
-void win_save(char* path) {
-    View* view = win_view(EDIT);
-    if (!path) path = view->buffer.path;
-    if (!path) return;
-    path = strdup(path);
-    free(view->buffer.path);
-    view->buffer.path = path;
-    buf_save(&(view->buffer));
+static void die(const char* msg) {
+    perror(msg);
+    exit(EXIT_FAILURE);
 }
 
-void win_loop(void) {
-    XMapWindow(X.display, X.self);
-    XFlush(X.display);
-    job_spawn(ConnectionNumber(X.display), xupdate, 0, 0);
-    while (1) job_poll(Timeout);
+/******************************************************************************/
+
+static void font_load(char* name) {
+    /* init the library and the base font pattern */
+    if (!FcInit())
+        die("Could not init fontconfig.\n");
+    FcPattern* pattern = FcNameParse((FcChar8 *)name);
+    if (!pattern)
+        die("could not parse font name\n");
+    /* load the base font */
+    FcResult result;
+    FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result);
+    if (!match || !(CurrFont.match = XftFontOpenPattern(X.display, match)))
+        die("could not load base font\n");
+    CurrFont.height = CurrFont.match->ascent + CurrFont.match->descent;
+    FcPatternDestroy(pattern);
+    FcPatternDestroy(match);
 }
 
-void win_quit(void) {
-    static uint64_t before = 0;
-    if (!win_buf(EDIT)->modified || (X.now - before) <= (uint64_t)ClickTime)
-        exit(0);
-    before = X.now;
+static size_t font_height(void) {
+    return CurrFont.match->height;
 }
 
-View* win_view(WinRegion id) {
-    return &(Regions[id == FOCUSED ? Focused : id]);
+static size_t font_width(void) {
+    XGlyphInfo extents;
+    XftTextExtentsUtf8(X.display, CurrFont.match, (const FcChar8*)"0", 1, &extents);
+    return extents.xOff;
 }
 
-Buf* win_buf(WinRegion id) {
-    return &(Regions[id == FOCUSED ? Focused : id].buffer);
+static size_t font_descent(void) {
+    return CurrFont.match->descent;
 }
 
 /******************************************************************************/
 
-bool x11_keymodsset(int mask) {
-    return ((KeyBtnState & mask) == mask);
+static void get_position(WinRegion id, int x, int y, size_t* row, size_t* col) {
+    int starty = (id == EDIT ? Divider+3 : 0);
+    int startx = (id == EDIT ? ScrollWidth+3 : 0);
+    *row = (y - starty) / font_height();
+    *col = (x - startx) / font_width();
+}
+
+static struct XSel* selfetch(Atom atom) {
+    for (int i = 0; i < (sizeof(Selections) / sizeof(Selections[0])); i++)
+        if (atom == Selections[i].atom)
+            return &Selections[i];
+    return NULL;
 }
 
 static void x11_window(char* name) {
@@ -209,11 +138,9 @@ static void x11_window(char* name) {
         X.height,
         0, X.depth,
         Palette[0]);
-
     /* register interest in the delete window message */
     Atom wmDeleteMessage = XInternAtom(X.display, "WM_DELETE_WINDOW", False);
     XSetWMProtocols(X.display, X.self, &wmDeleteMessage, 1);
-
     /* setup window attributes and events */
     XSetWindowAttributes swa;
     swa.backing_store = WhenMapped;
@@ -231,15 +158,12 @@ static void x11_window(char* name) {
         | PropertyChangeMask
         | ExposureMask
     );
-
     /* set input methods */
     if ((X.xim = XOpenIM(X.display, 0, 0, 0)))
         X.xic = XCreateIC(X.xim, XNInputStyle, XIMPreeditNothing|XIMStatusNothing, XNClientWindow, X.self, XNFocusWindow, X.self, NULL);
-
     /* initialize pixmap and drawing context */
     X.pixmap = XCreatePixmap(X.display, X.self, X.width, X.height, X.depth);
     X.xft    = XftDrawCreate(X.display, X.pixmap, X.visual, X.colormap);
-
     /* initialize the graphics context */
     XGCValues gcv;
     gcv.foreground = WhitePixel(X.display, X.screen);
@@ -249,35 +173,108 @@ static void x11_window(char* name) {
 
 /******************************************************************************/
 
-static void x11_font_load(char* name) {
-    /* init the library and the base font pattern */
-    if (!FcInit())
-        die("Could not init fontconfig.\n");
-    FcPattern* pattern = FcNameParse((FcChar8 *)name);
-    if (!pattern)
-        die("could not parse font name\n");
-    /* load the base font */
-    FcResult result;
-    FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result);
-    if (!match || !(CurrFont.match = XftFontOpenPattern(X.display, match)))
-        die("could not load base font\n");
-    CurrFont.height = CurrFont.match->ascent + CurrFont.match->descent;
-    FcPatternDestroy(pattern);
-    FcPatternDestroy(match);
+static uint32_t special_keys(uint32_t key) {
+    static uint32_t keymap[256] = {
+        /* Function keys */
+        [0xBE] = KEY_F1, [0xBF] = KEY_F2,  [0xC0] = KEY_F3,  [0xC1] = KEY_F4,
+        [0xC2] = KEY_F5, [0xC3] = KEY_F6,  [0xC4] = KEY_F7,  [0xC5] = KEY_F8,
+        [0xC6] = KEY_F9, [0xC7] = KEY_F10, [0xC8] = KEY_F11, [0xC9] = KEY_F12,
+        /* Navigation keys */
+        [0x50] = KEY_HOME,  [0x51] = KEY_LEFT, [0x52] = KEY_UP,
+        [0x53] = KEY_RIGHT, [0x54] = KEY_DOWN, [0x55] = KEY_PGUP,
+        [0x56] = KEY_PGDN,  [0x57] = KEY_END,
+        /* Control keys */
+        [0x08] = '\b', [0x09] = '\t', [0x0d] = '\n', [0x0a] = '\n',
+        /* Miscellaneous */
+        [0x63] = KEY_INSERT, [0x1B] = KEY_ESCAPE, [0xFF] = KEY_DELETE,
+    };
+    /* lookup the key by keysym */
+    key = ((key & 0xFF00) == 0xFF00 ? keymap[key & 0xFF] : key);
+    return (!key ? RUNE_ERR : key);
 }
 
-static size_t x11_font_height(void) {
-    return CurrFont.match->height;
+static uint32_t getkey(XEvent* e) {
+    char buf[8];
+    KeySym key;
+    Status status;
+    /* Read the key string */
+    if (X.xic)
+        Xutf8LookupString(X.xic, &(e->xkey), buf, sizeof(buf), &key, &status);
+    else
+        XLookupString(&(e->xkey), buf, sizeof(buf), &key, 0);
+    /* if it's ascii, just return it */
+    if (key >= 0x20 && key <= 0x7F)
+        return (uint32_t)key;
+    /* translate special key codes into unicode codepoints */
+    return special_keys(key);
 }
 
-static size_t x11_font_width(void) {
-    XGlyphInfo extents;
-    XftTextExtentsUtf8(X.display, CurrFont.match, (const FcChar8*)"0", 1, &extents);
-    return extents.xOff;
+static void mouse_left(WinRegion id, bool pressed, size_t row, size_t col) {
+    static int count = 0;
+    static Time before = 0;
+    if (!pressed) return;
+    count = ((X.now - before) <= (uint64_t)ClickTime ? count+1 : 1);
+    before = X.now;
+    if (PRESSED(MouseRight)) {
+        puts("fetch tag");
+    }  else if (PRESSED(MouseMiddle)) {
+        puts("exec with arg");
+    } else {
+        if (count == 1)
+            view_setcursor(win_view(id), row, col, win_keymodsset(ModShift));
+        else if (count == 2)
+            view_select(win_view(id), row, col);
+        else if (count == 3)
+            view_selword(win_view(id), row, col);
+    }
 }
 
-static size_t x11_font_descent(void) {
-    return CurrFont.match->descent;
+static void mouse_middle(WinRegion id, bool pressed, size_t row, size_t col) {
+    if (pressed) return;
+    if (PRESSED(MouseLeft)) {
+        cut(NULL);
+    } else {
+        char* str = view_fetch(win_view(id), row, col, riscmd);
+        if (str) exec(str);
+        free(str);
+    }
+}
+
+static void mouse_right(WinRegion id, bool pressed, size_t row, size_t col) {
+    if (pressed) return;
+    if (PRESSED(MouseLeft)) {
+        paste(NULL);
+    } else {
+        SearchDir *= (win_keymodsset(ModShift) ? -1 : +1);
+        free(SearchTerm);
+        SearchTerm = view_fetch(win_view(id), row, col, risfile);
+        view_findstr(win_view(id), SearchDir, SearchTerm);
+    }
+}
+
+static void mouse_click(int btn, bool pressed, int x, int y) {
+    size_t row, col;
+    Focused = (y <= Divider ? TAGS : EDIT);
+    get_position(Focused, x, y, &row, &col);
+    switch(btn) {
+        case MouseLeft:    mouse_left(Focused, pressed, row, col);    break;
+        case MouseMiddle:  mouse_middle(Focused, pressed, row, col);  break;
+        case MouseRight:   mouse_right(Focused, pressed, row, col);   break;
+        case MouseWheelUp: view_scroll(win_view(Focused), -ScrollBy); break;
+        case MouseWheelDn: view_scroll(win_view(Focused), +ScrollBy); break;
+    }
+}
+
+/******************************************************************************/
+
+static void xftcolor(XftColor* xc, int id) {
+    #define COLOR(c) ((c) | ((c) >> 8))
+    uint32_t c = Palette[id];
+    xc->color.alpha = 0xFFFF;
+    xc->color.red   = COLOR((c & 0x00FF0000) >> 8);
+    xc->color.green = COLOR((c & 0x0000FF00));
+    xc->color.blue  = COLOR((c & 0x000000FF) << 8);
+    XftColorAllocValue(X.display, X.visual, X.colormap, &(xc->color), xc);
 }
 
 static void getglyph(XGlyphSpec* spec, uint32_t rune) {
@@ -292,17 +289,24 @@ static size_t getglyphs(XGlyphSpec* specs, const XGlyph* glyphs, int len, int x,
         getglyph(&(specs[numspecs]), glyphs[i].rune);
         specs[numspecs].x = xp;
         specs[numspecs].y = yp;
-        xp += x11_font_width();
+        xp += font_width();
         numspecs++;
         i++;
         /* skip over null chars which mark multi column runes */
         for (; i < len && !glyphs[i].rune; i++)
-            xp += x11_font_width();
+            xp += font_width();
     }
     return numspecs;
 }
 
-static void x11_draw_glyphs(int fg, int bg, XGlyphSpec* specs, size_t nspecs, bool eol) {
+static void draw_rect(int color, int x, int y, int width, int height) {
+    XftColor clr;
+    xftcolor(&clr, color);
+    XftDrawRect(X.xft, &clr, x, y, width, height);
+    XftColorFree(X.display, X.visual, X.colormap, &clr);
+}
+
+static void place_glyphs(int fg, int bg, XGlyphSpec* specs, size_t nspecs, bool eol) {
     if (!nspecs) return;
     XftFont* font = specs[0].font;
     XftColor fgc, bgc;
@@ -314,7 +318,7 @@ static void x11_draw_glyphs(int fg, int bg, XGlyphSpec* specs, size_t nspecs, bo
         xftcolor(&bgc, bg);
         size_t width = specs[nspecs-1].x - specs[0].x + w;
         if (eol) width = X.width - specs[0].x;
-        x11_draw_rect(bg, specs[0].x, specs[0].y - h, width, font->height + LineSpacing);
+        draw_rect(bg, specs[0].x, specs[0].y - h, width, font->height + LineSpacing);
         XftColorFree(X.display, X.visual, X.colormap, &bgc);
     }
     xftcolor(&fgc, fg);
@@ -322,97 +326,87 @@ static void x11_draw_glyphs(int fg, int bg, XGlyphSpec* specs, size_t nspecs, bo
     XftColorFree(X.display, X.visual, X.colormap, &fgc);
 }
 
-static void x11_draw_rect(int color, int x, int y, int width, int height) {
-    XftColor clr;
-    xftcolor(&clr, color);
-    XftDrawRect(X.xft, &clr, x, y, width, height);
-    XftColorFree(X.display, X.visual, X.colormap, &clr);
-}
-
-/******************************************************************************/
-
-bool x11_sel_get(int selid, void(*cbfn)(char*)) {
-    struct XSel* sel = &(Selections[selid]);
-    if (sel->callback) return false;
-    Window owner = XGetSelectionOwner(X.display, sel->atom);
-    if (owner == X.self) {
-        cbfn(sel->text);
-    } else if (owner != None){
-        sel->callback = cbfn;
-        XConvertSelection(X.display, sel->atom, SelTarget, sel->atom, X.self, CurrentTime);
+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;
+    bool eol = false;
+    while (rlen && i < ncols) {
+        int numspecs = 0;
+        uint32_t attr = glyphs[i].attr;
+        while (i < ncols && glyphs[i].attr == attr) {
+            if (glyphs[i].rune == '\n')
+                glyphs[i].rune = ' ', eol = true;
+            getglyph(&(specs[numspecs]), glyphs[i].rune);
+            XGlyphInfo extents;
+            XftTextExtents32(X.display, CurrFont.match, &(const FcChar32){glyphs[i].rune}, 1, &extents);
+            specs[numspecs].x = x;
+            specs[numspecs].y = y - font_descent();
+            x += extents.xOff;
+            numspecs++;
+            i++;
+            /* skip over null chars which mark multi column runes */
+            for (; i < ncols && !glyphs[i].rune; i++)
+                x += font_width();
+        }
+        /* Draw the glyphs with the proper colors */
+        uint8_t bg = attr >> 8;
+        uint8_t fg = attr & 0xFF;
+        place_glyphs(fg, bg, specs, numspecs, eol);
+        eol = false, rlen -= numspecs;
     }
-    return true;
 }
 
-bool x11_sel_set(int selid, char* str) {
-    struct XSel* sel = &(Selections[selid]);
-    if (!sel || !str || !*str) {
-        free(str);
-        return false;
-    } else {
-        sel->text = str;
-        XSetSelectionOwner(X.display, sel->atom, X.self, CurrentTime);
-        return true;
+static void draw_view(int i, size_t nrows, drawcsr* csr, int bg, int fg, int sel) {
+    size_t fwidth = font_width();
+    size_t fheight = font_height();
+    size_t csrx = SIZE_MAX, csry = SIZE_MAX;
+    /* draw the view to the window */
+    View* view = win_view(i);
+    view_resize(view, nrows, (csr->w - csr->x) / fwidth);
+    view_update(view, (bg << 8 | fg), (sel << 8 | fg), &csrx, &csry);
+    draw_rect(bg, csr->x, csr->y, csr->w, (nrows * fheight) + 9);
+    for (size_t y = 0; y < view->nrows; y++) {
+        Row* row = view_getrow(view, y);
+        draw_glyphs(csr->x + 2, csr->y + 2 + ((y+1) * fheight), row->cols, row->rlen, row->len);
     }
+    /* place the cursor on screen */
+    if (!view_selsize(view) && csrx != SIZE_MAX && csry != SIZE_MAX) {
+        draw_rect(
+            (i == TAGS ? TagsCsr : EditCsr),
+            csr->x + 2 + (csrx * fwidth),
+            csr->y + 2 + (csry * fheight),
+            1, fheight);
+    }
+    csr->y += (nrows * fheight) + 3;
 }
 
-static struct XSel* selfetch(Atom atom) {
-    for (int i = 0; i < (sizeof(Selections) / sizeof(Selections[0])); i++)
-        if (atom == Selections[i].atom)
-            return &Selections[i];
-    return NULL;
-}
-
-/******************************************************************************/
-
-static WinRegion getregion(size_t x, size_t y) {
-    return (y <= Divider ? TAGS : EDIT);
-}
-
-static void xftcolor(XftColor* xc, int id) {
-    #define COLOR(c) ((c) | ((c) >> 8))
-    uint32_t c = Palette[id];
-    xc->color.alpha = 0xFFFF;
-    xc->color.red   = COLOR((c & 0x00FF0000) >> 8);
-    xc->color.green = COLOR((c & 0x0000FF00));
-    xc->color.blue  = COLOR((c & 0x000000FF) << 8);
-    XftColorAllocValue(X.display, X.visual, X.colormap, &(xc->color), xc);
-}
-
-static void get_position(WinRegion id, int x, int y, size_t* row, size_t* col) {
-    int starty = (id == EDIT ? Divider+3 : 0);
-    int startx = (id == EDIT ? ScrollWidth+3 : 0);
-    *row = (y - starty) / x11_font_height();
-    *col = (x - startx) / x11_font_width();
+static void draw_hrule(drawcsr* csr) {
+    draw_rect(HorBdr, csr->x, csr->y + 1, csr->w, 1);
+    Divider = csr->y;
+    csr->y += 2;
+    csr->x += ScrollWidth + 1;
 }
 
-/******************************************************************************/
-
-static void xupdate(Job* job) {
-    size_t fheight = x11_font_height();
-    size_t fwidth  = x11_font_width();
-    /* process events from the queue */
-    for (XEvent e; XPending(X.display);) {
-        XNextEvent(X.display, &e);
-        if (!XFilterEvent(&e, None) && EventHandlers[e.type])
-            (EventHandlers[e.type])(&e);
-    }
-    /* determine the size of the regions */
-    drawcsr csr = { .w = X.width, .h = X.height };
-    size_t maxtagrows = ((X.height - 2) / 4) / fheight;
-    size_t tagcols    = csr.w / fwidth;
-    size_t tagrows    = view_limitrows(win_view(TAGS), maxtagrows, tagcols);
-    size_t editrows   = ((X.height - 7) / fheight) - tagrows;
-    /* draw the regions to the window */
-    draw_view(TAGS, tagrows, &csr, TagsBg, TagsFg, TagsSel);
-    draw_hrule(&csr);
-    draw_view(EDIT, editrows, &csr, EditBg, EditFg, EditSel);
-    draw_scroll(&csr);
-    /* flush to the server */
-    XCopyArea(X.display, X.pixmap, X.self, X.gc, 0, 0, X.width, X.height, 0, 0);
-    XFlush(X.display);
+static void draw_scroll(drawcsr* csr) {
+    View* view = win_view(EDIT);
+    size_t bend = buf_end(win_buf(EDIT));
+    if (bend == 0) bend = 1;
+    if (!view->rows) return;
+    size_t vbeg = view->rows[0]->off;
+    size_t vend = view->rows[view->nrows-1]->off + view->rows[view->nrows-1]->rlen;
+    double scroll_vis = (double)(vend - vbeg) / (double)bend;
+    double scroll_off = ((double)vbeg / (double)bend);
+    size_t thumbreg = (csr->y - Divider) + 4;
+    size_t thumboff = (size_t)((thumbreg * scroll_off) + Divider);
+    size_t thumbsz  = (size_t)(thumbreg * scroll_vis);
+    if (thumbsz < 5) thumbsz = 5;
+    draw_rect(VerBdr,   ScrollWidth, Divider + 2,  1,           thumbreg);
+    draw_rect(ScrollBg, 0,           Divider + 2,  ScrollWidth, thumbreg);
+    draw_rect(ScrollFg, 0,           thumboff + 2, ScrollWidth, thumbsz);
 }
 
+/******************************************************************************/
+
 static void xfocus(XEvent* e) {
     if (X.xic)
         (e->type == FocusIn ? XSetICFocus : XUnsetICFocus)(X.xic);
@@ -420,7 +414,7 @@ static void xfocus(XEvent* e) {
 
 static void xkeypress(XEvent* e) {
     X.now = e->xkey.time;
-    Focused = getregion(e->xkey.x, e->xkey.y);
+    Focused = (e->xkey.y <= Divider ? TAGS : EDIT);
     uint32_t key = getkey(e);
     if (key == RUNE_ERR) return;
     KeyBtnState = e->xkey.state, LastKey = key;
@@ -518,8 +512,7 @@ static void xselrequest(XEvent* e) {
             SelTarget, 8, PropModeReplace,
             (unsigned char*)sel->text, strlen(sel->text));
     }
-    XSendEvent(X.display, s.xselection.requestor, True,
-0, &s);
+    XSendEvent(X.display, s.xselection.requestor, True, 0, &s);
 }
 
 static void xclientmsg(XEvent* e) {
@@ -536,182 +529,136 @@ static void xresize(XEvent* e) {
     }
 }
 
-/******************************************************************************/
+static void (*EventHandlers[LASTEvent])(XEvent*) = {
+    [FocusIn] = xfocus,
+    [FocusOut] = xfocus,
+    [KeyPress] = xkeypress,
+    [ButtonPress] = xbtnpress,
+    [ButtonRelease] = xbtnrelease,
+    [MotionNotify] = xbtnmotion,
+    [SelectionClear] = xselclear,
+    [SelectionNotify] = xselnotify,
+    [SelectionRequest] = xselrequest,
+    [ClientMessage] = xclientmsg,
+    [ConfigureNotify] = xresize,
+};
 
-static uint32_t special_keys(uint32_t key) {
-    static uint32_t keymap[256] = {
-        /* Function keys */
-        [0xBE] = KEY_F1, [0xBF] = KEY_F2,  [0xC0] = KEY_F3,  [0xC1] = KEY_F4,
-        [0xC2] = KEY_F5, [0xC3] = KEY_F6,  [0xC4] = KEY_F7,  [0xC5] = KEY_F8,
-        [0xC6] = KEY_F9, [0xC7] = KEY_F10, [0xC8] = KEY_F11, [0xC9] = KEY_F12,
-        /* Navigation keys */
-        [0x50] = KEY_HOME,  [0x51] = KEY_LEFT, [0x52] = KEY_UP,
-        [0x53] = KEY_RIGHT, [0x54] = KEY_DOWN, [0x55] = KEY_PGUP,
-        [0x56] = KEY_PGDN,  [0x57] = KEY_END,
-        /* Control keys */
-        [0x08] = '\b', [0x09] = '\t', [0x0d] = '\n', [0x0a] = '\n',
-        /* Miscellaneous */
-        [0x63] = KEY_INSERT, [0x1B] = KEY_ESCAPE, [0xFF] = KEY_DELETE,
-    };
-    /* lookup the key by keysym */
-    key = ((key & 0xFF00) == 0xFF00 ? keymap[key & 0xFF] : key);
-    return (!key ? RUNE_ERR : key);
+static void xupdate(Job* job) {
+    size_t fheight = font_height();
+    size_t fwidth  = font_width();
+    /* process events from the queue */
+    for (XEvent e; XPending(X.display);) {
+        XNextEvent(X.display, &e);
+        if (!XFilterEvent(&e, None) && EventHandlers[e.type])
+            (EventHandlers[e.type])(&e);
+    }
+    /* determine the size of the regions */
+    drawcsr csr = { .w = X.width, .h = X.height };
+    size_t maxtagrows = ((X.height - 2) / 4) / fheight;
+    size_t tagcols    = csr.w / fwidth;
+    size_t tagrows    = view_limitrows(win_view(TAGS), maxtagrows, tagcols);
+    size_t editrows   = ((X.height - 7) / fheight) - tagrows;
+    /* draw the regions to the window */
+    draw_view(TAGS, tagrows, &csr, TagsBg, TagsFg, TagsSel);
+    draw_hrule(&csr);
+    draw_view(EDIT, editrows, &csr, EditBg, EditFg, EditSel);
+    draw_scroll(&csr);
+    /* flush to the server */
+    XCopyArea(X.display, X.pixmap, X.self, X.gc, 0, 0, X.width, X.height, 0, 0);
+    XFlush(X.display);
 }
 
-static uint32_t getkey(XEvent* e) {
-    char buf[8];
-    KeySym key;
-    Status status;
-    /* Read the key string */
-    if (X.xic)
-        Xutf8LookupString(X.xic, &(e->xkey), buf, sizeof(buf), &key, &status);
-    else
-        XLookupString(&(e->xkey), buf, sizeof(buf), &key, 0);
-    /* if it's ascii, just return it */
-    if (key >= 0x20 && key <= 0x7F)
-        return (uint32_t)key;
-    /* translate special key codes into unicode codepoints */
-    return special_keys(key);
-}
+/******************************************************************************/
 
-static void mouse_click(int btn, bool pressed, int x, int y) {
-    size_t row, col;
-    Focused = getregion(x, y);
-    get_position(Focused, x, y, &row, &col);
-    switch(btn) {
-        case MouseLeft:    mouse_left(Focused, pressed, row, col);    break;
-        case MouseMiddle:  mouse_middle(Focused, pressed, row, col);  break;
-        case MouseRight:   mouse_right(Focused, pressed, row, col);   break;
-        case MouseWheelUp: view_scroll(win_view(Focused), -ScrollBy); break;
-        case MouseWheelDn: view_scroll(win_view(Focused), +ScrollBy); break;
-    }
+void win_init(char* title, KeyBinding* bindings) {
+    Keys = bindings;
+    view_init(&Regions[TAGS], NULL);
+    view_init(&Regions[EDIT], NULL);
+    signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal
+    setlocale(LC_CTYPE, "");
+    XSetLocaleModifiers("");
+    /* open the X display and get basic attributes */
+    if (!(X.display = XOpenDisplay(0)))
+        die("could not open display");
+    X.root = DefaultRootWindow(X.display);
+    XWindowAttributes wa;
+    XGetWindowAttributes(X.display, X.root, &wa);
+    X.visual   = wa.visual;
+    X.colormap = wa.colormap;
+    X.screen   = DefaultScreen(X.display);
+    X.depth    = DefaultDepth(X.display, X.screen);
+    font_load(FontString);
+    x11_window("tide");
+    /* initialize selection atoms */
+    for (int i = 0; i < (sizeof(Selections) / sizeof(Selections[0])); i++)
+        Selections[i].atom = XInternAtom(X.display, Selections[i].name, 0);
+    SelTarget = XInternAtom(X.display, "UTF8_STRING", 0);
+    if (SelTarget == None)
+        SelTarget = XInternAtom(X.display, "STRING", 0);
+    /* Populate the  tags region */
+    View* view = win_view(TAGS);
+    view_putstr(view, TagString);
+    view_selprev(view); // clear the selection
+    buf_logclear(&(view->buffer));
 }
 
-static void mouse_left(WinRegion id, bool pressed, size_t row, size_t col) {
-    static int count = 0;
-    static Time before = 0;
-    if (!pressed) return;
-    count = ((X.now - before) <= (uint64_t)ClickTime ? count+1 : 1);
-    before = X.now;
-    if (PRESSED(MouseRight)) {
-        puts("fetch tag");
-    }  else if (PRESSED(MouseMiddle)) {
-        puts("exec with arg");
-    } else {
-        if (count == 1)
-            view_setcursor(win_view(id), row, col, x11_keymodsset(ModShift));
-        else if (count == 2)
-            view_select(win_view(id), row, col);
-        else if (count == 3)
-            view_selword(win_view(id), row, col);
-    }
+void win_save(char* path) {
+    View* view = win_view(EDIT);
+    if (!path) path = view->buffer.path;
+    if (!path) return;
+    path = strdup(path);
+    free(view->buffer.path);
+    view->buffer.path = path;
+    buf_save(&(view->buffer));
 }
 
-static void mouse_middle(WinRegion id, bool pressed, size_t row, size_t col) {
-    if (pressed) return;
-    if (PRESSED(MouseLeft)) {
-        cut(NULL);
-    } else {
-        char* str = view_fetch(win_view(id), row, col, riscmd);
-        if (str) exec(str);
-        free(str);
-    }
+void win_loop(void) {
+    XMapWindow(X.display, X.self);
+    XFlush(X.display);
+    job_spawn(ConnectionNumber(X.display), xupdate, 0, 0);
+    while (1) job_poll(Timeout);
 }
 
-static void mouse_right(WinRegion id, bool pressed, size_t row, size_t col) {
-    if (pressed) return;
-    if (PRESSED(MouseLeft)) {
-        paste(NULL);
-    } else {
-        SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
-        free(SearchTerm);
-        SearchTerm = view_fetch(win_view(id), row, col, risfile);
-        view_findstr(win_view(id), SearchDir, SearchTerm);
-    }
+void win_quit(void) {
+    static uint64_t before = 0;
+    if (!win_buf(EDIT)->modified || (X.now - before) <= (uint64_t)ClickTime)
+        exit(0);
+    before = X.now;
 }
 
-/******************************************************************************/
-
-static void draw_view(int i, size_t nrows, drawcsr* csr, int bg, int fg, int sel) {
-    size_t fwidth = x11_font_width();
-    size_t fheight = x11_font_height();
-    size_t csrx = SIZE_MAX, csry = SIZE_MAX;
-    /* draw the view to the window */
-    View* view = win_view(i);
-    view_resize(view, nrows, (csr->w - csr->x) / fwidth);
-    view_update(view, (bg << 8 | fg), (sel << 8 | fg), &csrx, &csry);
-    x11_draw_rect(bg, csr->x, csr->y, csr->w, (nrows * fheight) + 9);
-    for (size_t y = 0; y < view->nrows; y++) {
-        Row* row = view_getrow(view, y);
-        draw_glyphs(csr->x + 2, csr->y + 2 + ((y+1) * fheight), row->cols, row->rlen, row->len);
-    }
-    /* place the cursor on screen */
-    if (!view_selsize(view) && csrx != SIZE_MAX && csry != SIZE_MAX) {
-        x11_draw_rect(
-            (i == TAGS ? TagsCsr : EditCsr),
-            csr->x + 2 + (csrx * fwidth),
-            csr->y + 2 + (csry * fheight),
-            1, fheight);
-    }
-    csr->y += (nrows * fheight) + 3;
+View* win_view(WinRegion id) {
+    return &(Regions[id == FOCUSED ? Focused : id]);
 }
 
-static void draw_hrule(drawcsr* csr) {
-    x11_draw_rect(HorBdr, csr->x, csr->y + 1, csr->w, 1);
-    Divider = csr->y;
-    csr->y += 2;
-    csr->x += ScrollWidth + 1;
+Buf* win_buf(WinRegion id) {
+    return &(Regions[id == FOCUSED ? Focused : id].buffer);
 }
 
-static void draw_scroll(drawcsr* csr) {
-    View* view = win_view(EDIT);
-    size_t bend = buf_end(win_buf(EDIT));
-    if (bend == 0) bend = 1;
-    if (!view->rows) return;
-    size_t vbeg = view->rows[0]->off;
-    size_t vend = view->rows[view->nrows-1]->off + view->rows[view->nrows-1]->rlen;
-    double scroll_vis = (double)(vend - vbeg) / (double)bend;
-    double scroll_off = ((double)vbeg / (double)bend);
-    size_t thumbreg = (csr->y - Divider) + 4;
-    size_t thumboff = (size_t)((thumbreg * scroll_off) + Divider);
-    size_t thumbsz  = (size_t)(thumbreg * scroll_vis);
-    if (thumbsz < 5) thumbsz = 5;
-    x11_draw_rect(VerBdr,   ScrollWidth, Divider + 2,  1,           thumbreg);
-    x11_draw_rect(ScrollBg, 0,           Divider + 2,  ScrollWidth, thumbreg);
-    x11_draw_rect(ScrollFg, 0,           thumboff + 2, ScrollWidth, thumbsz);
+bool win_keymodsset(int mask) {
+    return ((KeyBtnState & mask) == mask);
 }
 
-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;
-    bool eol = false;
-    while (rlen && i < ncols) {
-        int numspecs = 0;
-        uint32_t attr = glyphs[i].attr;
-        while (i < ncols && glyphs[i].attr == attr) {
-            if (glyphs[i].rune == '\n')
-                glyphs[i].rune = ' ', eol = true;
-            getglyph(&(specs[numspecs]), glyphs[i].rune);
-            XGlyphInfo extents;
-            XftTextExtents32(X.display, CurrFont.match, &(const FcChar32){glyphs[i].rune}, 1, &extents);
-            specs[numspecs].x = x;
-            specs[numspecs].y = y - x11_font_descent();
-            x += extents.xOff;
-            numspecs++;
-            i++;
-            /* skip over null chars which mark multi column runes */
-            for (; i < ncols && !glyphs[i].rune; i++)
-                x += x11_font_width();
-        }
-        /* Draw the glyphs with the proper colors */
-        uint8_t bg = attr >> 8;
-        uint8_t fg = attr & 0xFF;
-        x11_draw_glyphs(fg, bg, specs, numspecs, eol);
-        eol = false, rlen -= numspecs;
+bool win_sel_get(int selid, void(*cbfn)(char*)) {
+    struct XSel* sel = &(Selections[selid]);
+    if (sel->callback) return false;
+    Window owner = XGetSelectionOwner(X.display, sel->atom);
+    if (owner == X.self) {
+        cbfn(sel->text);
+    } else if (owner != None){
+        sel->callback = cbfn;
+        XConvertSelection(X.display, sel->atom, SelTarget, sel->atom, X.self, CurrentTime);
     }
+    return true;
 }
 
-static void die(const char* msg) {
-    perror(msg);
-    exit(EXIT_FAILURE);
+bool win_sel_set(int selid, char* str) {
+    struct XSel* sel = &(Selections[selid]);
+    if (!sel || !str || !*str) {
+        free(str);
+        return false;
+    } else {
+        sel->text = str;
+        XSetSelectionOwner(X.display, sel->atom, X.self, CurrentTime);
+        return true;
+    }
 }
diff --git a/tide.c b/tide.c
index 80a3b2cb52689d85d4fb3aaed26ff0068760746f..33725eeb560b3a48682e2dbf713077fb24c40da3 100644 (file)
--- a/tide.c
+++ b/tide.c
@@ -139,7 +139,7 @@ static void join_lines(char* arg) {
 }
 
 static void delete(char* arg) {
-    bool byword = x11_keymodsset(ModCtrl);
+    bool byword = win_keymodsset(ModCtrl);
     view_delete(win_view(FOCUSED), RIGHT, byword);
 }
 
@@ -154,12 +154,12 @@ void cut(char* arg) {
         select_line(arg);
     /* now perform the cut */
     char* str = view_getstr(view);
-    x11_sel_set(CLIPBOARD, str);
+    win_sel_set(CLIPBOARD, str);
     if (str && *str) delete(arg);
 }
 
 void paste(char* arg) {
-    assert(x11_sel_get(CLIPBOARD, onpaste));
+    assert(win_sel_get(CLIPBOARD, onpaste));
 }
 
 static void copy(char* arg) {
@@ -167,7 +167,7 @@ static void copy(char* arg) {
     if (!view_selsize(win_view(FOCUSED)))
         select_line(arg);
     char* str = view_getstr(win_view(FOCUSED));
-    x11_sel_set(CLIPBOARD, str);
+    win_sel_set(CLIPBOARD, str);
 }
 
 static void del_to(void (*tofn)(View*, bool)) {
@@ -191,7 +191,7 @@ static void del_to_bow(char* arg) {
 }
 
 static void backspace(char* arg) {
-    view_delete(win_view(FOCUSED), LEFT, x11_keymodsset(ModCtrl));
+    view_delete(win_view(FOCUSED), LEFT, win_keymodsset(ModCtrl));
 }
 
 static void cursor_bol(char* arg) {
@@ -203,15 +203,15 @@ static void cursor_eol(char* arg) {
 }
 
 static void cursor_mvlr(int dir) {
-    bool extsel = x11_keymodsset(ModShift);
-    if (x11_keymodsset(ModCtrl))
+    bool extsel = win_keymodsset(ModShift);
+    if (win_keymodsset(ModCtrl))
         view_byword(win_view(FOCUSED), dir, extsel);
     else
         view_byrune(win_view(FOCUSED), dir, extsel);
 }
 
 static void cursor_mvupdn(int dir) {
-    bool extsel = x11_keymodsset(ModShift);
+    bool extsel = win_keymodsset(ModShift);
     view_byline(win_view(FOCUSED), dir, extsel);
 }
 
@@ -219,8 +219,8 @@ static void cursor_home_end(
     void (*docfn)(View*, bool),
     void (*linefn)(View*, bool)
 ) {
-    bool extsel = x11_keymodsset(ModShift);
-    if (x11_keymodsset(ModCtrl))
+    bool extsel = win_keymodsset(ModShift);
+    if (win_keymodsset(ModCtrl))
         docfn(win_view(FOCUSED), extsel);
     else
         linefn(win_view(FOCUSED), extsel);
@@ -303,8 +303,8 @@ static void tag_redo(char* arg) {
 
 static void search(char* arg) {
     char* str;
-    SearchDir *= (x11_keymodsset(ModShift) ? UP : DOWN);
-    if (x11_keymodsset(ModAlt) && SearchTerm)
+    SearchDir *= (win_keymodsset(ModShift) ? UP : DOWN);
+    if (win_keymodsset(ModAlt) && SearchTerm)
         str = strdup(SearchTerm);
     else
         str = view_getctx(win_view(FOCUSED));
@@ -320,7 +320,7 @@ static void execute(char* arg) {
 }
 
 static void find(char* arg) {
-    SearchDir *= (x11_keymodsset(ModShift) ? UP : DOWN);
+    SearchDir *= (win_keymodsset(ModShift) ? UP : DOWN);
     view_findstr(win_view(EDIT), SearchDir, arg);
 }
 
@@ -379,7 +379,7 @@ static void new_win(char* arg) {
 
 static void newline(char* arg) {
     View* view = win_view(FOCUSED);
-    if (x11_keymodsset(ModShift)) {
+    if (win_keymodsset(ModShift)) {
         view_byline(view, UP, false);
         view_bol(view, false);
     }