From f7368d6c794a1f3fb42f585afac773bf1f9c0c1b Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Thu, 12 Apr 2018 21:54:09 -0400 Subject: [PATCH] refactor and reorg x11.c --- inc/win.h | 7 +- lib/x11.c | 749 +++++++++++++++++++++++++----------------------------- tide.c | 28 +- 3 files changed, 365 insertions(+), 419 deletions(-) diff --git a/inc/win.h b/inc/win.h index 43c7226..932f4d6 100644 --- 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; diff --git a/lib/x11.c b/lib/x11.c index 09e3e8b..c5d4dd5 100644 --- 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 80a3b2c..33725ee 100644 --- 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); } -- 2.51.0