From 1b9615343c191f455236284fa2059108b8d3b1ca Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Thu, 22 Mar 2018 21:43:44 -0400 Subject: [PATCH] combine win and x11 to try and slim down --- Makefile | 7 +- inc/win.h | 1 - lib/win.c | 440 ---------------------------------------------------- lib/x11.c | 452 ++++++++++++++++++++++++++++++++++++++++++++++++------ tide.c | 8 - 5 files changed, 410 insertions(+), 498 deletions(-) diff --git a/Makefile b/Makefile index edb8149..347207d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ INCS = -Iinc/ - -BINS = tide tfetch tctl pick xcpd +MAKEFLAGS = -j4 +BINS = tide MAN1 = docs/tide.1 docs/pick.1 docs/picktag.1 docs/pickfile.1 LIBEDIT_OBJS = \ @@ -11,7 +11,6 @@ LIBEDIT_OBJS = \ lib/job.o \ lib/view.o \ lib/x11.o \ - lib/win.o \ lib/colors.o \ lib/config.o @@ -24,7 +23,7 @@ include config.mk .PHONY: all docs clean install uninstall test -all: $(BINS) test $(MAN1) +all: $(BINS) $(MAN1) docs: ronn --roff docs/*.md diff --git a/inc/win.h b/inc/win.h index 9c9a39f..9a0c326 100644 --- a/inc/win.h +++ b/inc/win.h @@ -25,7 +25,6 @@ typedef struct { size_t height, width; size_t csrx, csry; CPair clrnor, clrsel, clrcsr; - bool warp_ptr; View view; } Region; diff --git a/lib/win.c b/lib/win.c index 9a60798..8b13789 100644 --- a/lib/win.c +++ b/lib/win.c @@ -1,441 +1 @@ -#define _POSIX_C_SOURCE 200809L -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include -#include -#include -static void onredraw(int height, int width); -static void oninput(int mods, Rune key); -static void onmousedrag(int state, int x, int y); -static void onmousebtn(int btn, bool pressed, int x, int y); -static void onwheelup(WinRegion id, bool pressed, size_t row, size_t col); -static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col); -static void oncommand(char* cmd); -static bool update_focus(void); -static void draw_line_num(bool current, size_t x, size_t y, size_t gcols, size_t num); -static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols); -static WinRegion getregion(size_t x, size_t y); - -static size_t Ruler = 0; -static double ScrollOffset = 0.0; -static double ScrollVisible = 1.0; -static XFont Font; -static XConfig Config = { - .redraw = onredraw, - .handle_key = oninput, - .shutdown = onshutdown, - .set_focus = onfocus, - .mouse_drag = onmousedrag, - .mouse_btn = onmousebtn, - .cmd_received = oncommand -}; -static WinRegion Focused = EDIT; -static Region Regions[NREGIONS] = {0}; -static Rune LastKey; -static KeyBinding* Keys = NULL; -static void (*InputFunc)(Rune); - -static void win_init(void (*errfn)(char*)) { - for (int i = 0; i < SCROLL; i++) - view_init(&(Regions[i].view), NULL, errfn); - x11_init(&Config); - Font = x11_font_load(FontString); - Regions[SCROLL].clrnor = Colors[ClrScrollNor]; - Regions[TAGS].clrnor = Colors[ClrTagsNor]; - Regions[TAGS].clrsel = Colors[ClrTagsSel]; - Regions[TAGS].clrcsr = Colors[ClrTagsCsr]; - Regions[EDIT].clrnor = Colors[ClrEditNor]; - Regions[EDIT].clrsel = Colors[ClrEditSel]; - Regions[EDIT].clrcsr = Colors[ClrEditCsr]; -} - -void win_window(char* name, bool isdialog, void (*errfn)(char*)) { - win_init(errfn); - if (isdialog) - x11_dialog(name, WinWidth, WinHeight); - else - x11_window(name, WinWidth, WinHeight); -} - -static void win_update(int xfd, void* data) { - if (xfd < 0) return; - if (x11_events_queued()) - x11_events_take(); - x11_flush(); -} - -static void set_path_prop(char* path) { -#if 0 - char pathbuf[PATH_MAX] = {0}; - if (!path) return; - char *abspath = realpath(path, pathbuf); - if (!abspath) return; - x11_prop_set("TIDE_FILE", abspath); -#endif -} - -void win_load(char* path) { - View* view = win_view(EDIT); - view_init(view, path, view->buffer.errfn); - path = view->buffer.path; - if (path) set_path_prop(path); -} - -void win_save(char* path) { - View* view = win_view(EDIT); - if (!path) path = view->buffer.path; - if (!path) return; - path = stringdup(path); - free(view->buffer.path); - view->buffer.path = path; - buf_save(&(view->buffer)); - set_path_prop(path); -} - -void win_loop(void) { - x11_show(); - while (x11_running()) { - bool pending = job_poll(x11_connfd(), Timeout); - int nevents = x11_events_queued(); - if (update_focus() || pending || nevents) { - x11_events_take(); - if (x11_running()) - x11_flip(); - } - x11_flush(); - } - x11_finish(); -} - -void win_settext(WinRegion id, char* text) { - View* view = win_view(id); - view->buffer.gapstart = view->buffer.bufstart; - view->buffer.gapend = view->buffer.bufend; - view->selection = (Sel){0,0,0}; - view_putstr(view, text); - view_selprev(view); // clear the selection - buf_logclear(&(view->buffer)); -} - -void win_setruler(size_t ruler) { - Ruler = ruler; -} - -Rune win_getkey(void) { - return LastKey; -} - -void win_setkeys(KeyBinding* bindings, void (*inputfn)(Rune)) { - Keys = bindings; - InputFunc = inputfn; -} - -bool win_btnpressed(int btn) { - int btnmask = (1 << (btn + 7)); - return ((x11_keybtnstate() & btnmask) == btnmask); -} - -WinRegion win_getregion(void) { - return Focused; -} - -bool win_setregion(WinRegion id) { - bool changed = true; - if (Focused != id && (id == TAGS || id == EDIT)) - changed = true, Focused = id; - return changed; -} - -void win_warpptr(WinRegion id) { - Regions[id].warp_ptr = true; -} - -View* win_view(WinRegion id) { - if (id == FOCUSED) id = Focused; - return &(Regions[id].view); -} - -Buf* win_buf(WinRegion id) { - if (id == FOCUSED) id = Focused; - return &(Regions[id].view.buffer); -} - -Sel* win_sel(WinRegion id) { - if (id == FOCUSED) id = Focused; - return &(Regions[id].view.selection); -} - -void win_setscroll(double offset, double visible) { - ScrollOffset = offset; - ScrollVisible = visible; -} - -static void layout(int width, int height) { - size_t fheight = x11_font_height(Font); - size_t fwidth = x11_font_width(Font); - View* tagview = win_view(TAGS); - View* editview = win_view(EDIT); - - /* update the text views and region positions and sizes */ - for (int i = 0; i < SCROLL; i++) { - Regions[i].x = 2; - Regions[i].y = 2; - Regions[i].csrx = SIZE_MAX; - Regions[i].csry = SIZE_MAX; - Regions[i].width = (width - 4); - Regions[i].height = fheight; - } - - /* Place the tag region relative to status */ - size_t maxtagrows = ((height - Regions[TAGS].y - 5) / 4) / fheight; - size_t tagcols = Regions[TAGS].width / fwidth; - size_t tagrows = view_limitrows(tagview, maxtagrows, tagcols); - Regions[TAGS].height = tagrows * fheight; - view_resize(tagview, tagrows, tagcols); - - /* Place the scroll region relative to tags */ - Regions[SCROLL].x = 0; - Regions[SCROLL].y = 5 + Regions[TAGS].y + Regions[TAGS].height; - Regions[SCROLL].height = (height - Regions[EDIT].y - 5); - Regions[SCROLL].width = 5 + fwidth; - - /* Place the edit region relative to tags */ - Regions[EDIT].x = 3 + Regions[SCROLL].width; - Regions[EDIT].y = 5 + Regions[TAGS].y + Regions[TAGS].height; - Regions[EDIT].height = (height - Regions[EDIT].y - 5); - Regions[EDIT].width = width - Regions[SCROLL].width - 5; - view_resize(editview, Regions[EDIT].height / fheight, Regions[EDIT].width / fwidth); -} - -static void onredraw(int width, int height) { - size_t fheight = x11_font_height(Font); - size_t fwidth = x11_font_width(Font); - - layout(width, height); - onupdate(); // Let the user program update the status and other content - int clrtagnor = (Regions[TAGS].clrnor.bg << 8 | Regions[TAGS].clrnor.fg); - int clrtagsel = (Regions[TAGS].clrsel.bg << 8 | Regions[TAGS].clrsel.fg); - view_update(win_view(TAGS), clrtagnor, clrtagsel, &(Regions[TAGS].csrx), &(Regions[TAGS].csry)); - int clreditnor = (Regions[EDIT].clrnor.bg << 8 | Regions[EDIT].clrnor.fg); - int clreditsel = (Regions[EDIT].clrsel.bg << 8 | Regions[EDIT].clrsel.fg); - view_update(win_view(EDIT), clreditnor, clreditsel, &(Regions[EDIT].csrx), &(Regions[EDIT].csry)); - onlayout(); // Let the user program update the scroll bar - - int clr_hbor = Colors[ClrBorders].bg; - int clr_vbor = Colors[ClrBorders].bg; - CPair clr_scroll = Colors[ClrScrollNor]; - - for (int i = 0; i < SCROLL; i++) { - View* view = win_view(i); - x11_draw_rect(Regions[i].clrnor.bg, 0, Regions[i].y - 3, width, Regions[i].height + 8); - x11_draw_rect(clr_hbor, 0, Regions[i].y - 3, width, 1); - - if (i == EDIT) { - if (Ruler) - x11_draw_rect( Colors[ClrEditRul].fg, - ((Ruler+2) * fwidth), - Regions[i].y-2, - 1, - Regions[i].height+7 ); - } - - for (size_t line = 0, y = 0; y < view->nrows; y++) { - Row* row = view_getrow(view, y); - draw_glyphs(Regions[i].x, Regions[i].y + ((y+1) * fheight), row->cols, row->rlen, row->len); - } - } - - /* draw the scroll region */ - size_t thumbreg = (Regions[SCROLL].height - Regions[SCROLL].y + 9); - size_t thumboff = (size_t)((thumbreg * ScrollOffset) + (Regions[SCROLL].y - 2)); - size_t thumbsz = (size_t)(thumbreg * ScrollVisible); - if (thumbsz < 5) thumbsz = 5; - x11_draw_rect(clr_vbor, Regions[SCROLL].width, Regions[SCROLL].y - 2, 1, Regions[SCROLL].height); - x11_draw_rect(clr_scroll.bg, 0, Regions[SCROLL].y - 2, Regions[SCROLL].width, thumbreg); - x11_draw_rect(clr_scroll.fg, 0, thumboff, Regions[SCROLL].width, thumbsz); - - /* place the cursor on screen */ - if (Regions[Focused].csrx != SIZE_MAX && Regions[Focused].csry != SIZE_MAX) { - x11_draw_rect( - Regions[Focused].clrcsr.fg, - Regions[Focused].x + (Regions[Focused].csrx * fwidth), - Regions[Focused].y + (Regions[Focused].csry * fheight), - 1, fheight); - } - - /* adjust the mouse location */ - if (Regions[Focused].warp_ptr) { - Regions[Focused].warp_ptr = false; - size_t x = Regions[Focused].x + (Regions[Focused].csrx * fwidth) - (fwidth/2); - size_t y = Regions[Focused].y + (Regions[Focused].csry * fheight) + (fheight/2); - x11_mouse_set(x, y); - } -} - -static void oninput(int mods, Rune key) { - LastKey = key; - /* mask of modifiers we don't care about */ - mods = mods & (ModCtrl|ModShift|ModAlt); - /* handle the proper line endings */ - if (key == '\r') key = '\n'; - /* search for a key binding entry */ - uint32_t mkey = tolower(key); - for (KeyBinding* bind = Keys; bind && bind->key; bind++) { - bool match = (mkey == bind->key); - bool exact = (bind->mods == mods); - bool any = (bind->mods == ModAny); - bool oneplus = ((bind->mods == ModOneOrMore) && (mods & ModOneOrMore)); - if (match && (exact || oneplus || any)) { - bind->action(); - return; - } - } - - /* translate to crlf if needed */ - if (key == '\n' && win_view(FOCUSED)->buffer.crlf) - key = RUNE_CRLF; - - /* 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) { - if (InputFunc) - InputFunc(key); - else - view_insert(win_view(FOCUSED), true, key); - } -} - -static void scroll_actions(int btn, bool pressed, int x, int y) { - size_t row = (y-Regions[SCROLL].y) / x11_font_height(Font); - switch (btn) { - case MouseLeft: - if (pressed) - view_scroll(win_view(EDIT), -row); - break; - case MouseMiddle: - if (pressed) - onscroll((double)(y - Regions[SCROLL].y) / - (double)(Regions[SCROLL].height - Regions[SCROLL].y)); - break; - case MouseRight: - if (pressed) - view_scroll(win_view(EDIT), +row); - break; - case MouseWheelUp: - view_scroll(win_view(EDIT), -ScrollBy); - break; - case MouseWheelDn: - view_scroll(win_view(EDIT), +ScrollBy); - break; - } -} - -static void onmousedrag(int state, int x, int y) { - if (x < Regions[Focused].x) x = Regions[Focused].x; - if (y < Regions[Focused].y) y = Regions[Focused].y; - size_t row = (y-Regions[Focused].y) / x11_font_height(Font); - size_t col = (x-Regions[Focused].x) / x11_font_width(Font); - if (win_btnpressed(MouseLeft)) - view_selext(win_view(Focused), row, col); -} - -static void onmousebtn(int btn, bool pressed, int x, int y) { - WinRegion id = getregion(x, y); - if (id == FOCUSED && x < Regions[Focused].x) - x = Regions[Focused].x, id = getregion(x, y); - size_t row = (y-Regions[id].y) / x11_font_height(Font); - size_t col = (x-Regions[id].x) / x11_font_width(Font); - - if (id == SCROLL) { - scroll_actions(btn, pressed, x, y); - } else { - switch(btn) { - case MouseLeft: onmouseleft(id, pressed, row, col); break; - case MouseMiddle: onmousemiddle(id, pressed, row, col); break; - case MouseRight: onmouseright(id, pressed, row, col); break; - case MouseWheelUp: onwheelup(id, pressed, row, col); break; - case MouseWheelDn: onwheeldn(id, pressed, row, col); break; - } - } -} - -static void onwheelup(WinRegion id, bool pressed, size_t row, size_t col) { - if (!pressed) return; - view_scroll(win_view(id), -ScrollBy); -} - -static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col) { - if (!pressed) return; - view_scroll(win_view(id), +ScrollBy); -} - -static void oncommand(char* cmd) { - size_t line = strtoul(cmd, NULL, 0); - if (line) { - View* view = win_view(EDIT); - win_setregion(EDIT); - view_setln(view, line); - view_eol(view, false); - view_selctx(view); - } -} - -static bool update_focus(void) { - return false; -#if 0 - static int prev_x = 0, prev_y = 0; - int ptr_x, ptr_y; - bool changed = false; - /* dont change focus if any mouse buttons are pressed */ - if ((x11_keybtnstate() & 0x1f00) == 0) { - x11_mouse_get(&ptr_x, &ptr_y); - if (prev_x != ptr_x || prev_y != ptr_y) - changed = win_setregion(getregion(ptr_x, ptr_y)); - prev_x = ptr_x, prev_y = ptr_y; - } - return changed; -#endif -} - -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; - 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, eol); - eol = false, rlen -= numspecs; - } -} - -static WinRegion 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 (WinRegion)i; - } - return NREGIONS; -} diff --git a/lib/x11.c b/lib/x11.c index 8d7385f..299ce09 100644 --- a/lib/x11.c +++ b/lib/x11.c @@ -1,21 +1,39 @@ -#include -#include -#include +#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 700 +#include +#include #include #include #include #include +#include #include #include #include +#include + +#define Region _Region +#include +#include +#include +#undef Region enum { FontCacheSize = 16 }; +static void onredraw(int height, int width); +static void oninput(int mods, Rune key); +static void onmousedrag(int state, int x, int y); +static void onmousebtn(int btn, bool pressed, int x, int y); +static void onwheelup(WinRegion id, bool pressed, size_t row, size_t col); +static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col); +static void oncommand(char* cmd); +static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols); +static WinRegion getregion(size_t x, size_t y); + static struct XSel* selfetch(Atom atom); static void selclear(XEvent* evnt); static void selnotify(XEvent* evnt); static void selrequest(XEvent* evnt); -static void propnotify(XEvent* evnt); static char* readprop(Display* disp, Window win, Atom prop); struct XFont { @@ -53,7 +71,6 @@ static struct { XIM xim; GC gc; } X; -static XConfig* Config; static int KeyBtnState; static Atom SelTarget; static struct XSel { @@ -66,6 +83,27 @@ static struct XSel { { .name = "CLIPBOARD" }, }; +static size_t Ruler = 0; +static double ScrollOffset = 0.0; +static double ScrollVisible = 1.0; +static XFont CurrFont; +static XConfig Config = { + .redraw = onredraw, + .handle_key = oninput, + .shutdown = onshutdown, + .set_focus = onfocus, + .mouse_drag = onmousedrag, + .mouse_btn = onmousebtn, + .cmd_received = oncommand +}; +static WinRegion Focused = EDIT; +static Region Regions[NREGIONS] = {0}; +static Rune LastKey; +static KeyBinding* Keys = NULL; +static void (*InputFunc)(Rune); + + + static void xftcolor(XftColor* xc, int id) { #define COLOR(c) ((c) | ((c) >> 8)) uint32_t c = Palette[id]; @@ -86,7 +124,6 @@ void x11_init(XConfig* cfg) { setlocale(LC_CTYPE, ""); XSetLocaleModifiers(""); /* open the X display and get basic attributes */ - Config = cfg; if (!(X.display = XOpenDisplay(0))) die("could not open display"); X.root = DefaultRootWindow(X.display); @@ -105,10 +142,6 @@ void x11_init(XConfig* cfg) { SelTarget = XInternAtom(X.display, "STRING", 0); } -int x11_connfd(void) { - return ConnectionNumber(X.display); -} - int x11_keybtnstate(void) { return KeyBtnState; } @@ -165,12 +198,6 @@ void x11_window(char* name, int width, int height) { gcv.foreground = WhitePixel(X.display, X.screen); gcv.graphics_exposures = False; X.gc = XCreateGC(X.display, X.self, GCForeground|GCGraphicsExposures, &gcv); - - /* Create and updtate tide-specific properties */ - Atom prop = XInternAtom(X.display, "TIDE_WINDOWS", False); - XChangeProperty(X.display, X.root, prop, XA_WINDOW, 32, PropModeAppend, (unsigned char*)&X.self, 1); - prop = XInternAtom(X.display, "TIDE_COMM", False); - XChangeProperty(X.display, X.self, prop, XA_STRING, 8, PropModeReplace, (unsigned char*)"", 0); } void x11_dialog(char* name, int height, int width) { @@ -195,7 +222,7 @@ bool x11_running(void) { } void x11_flip(void) { - Config->redraw(X.width, X.height); + Config.redraw(X.width, X.height); XCopyArea(X.display, X.pixmap, X.self, X.gc, 0, 0, X.width, X.height, 0, 0); x11_flush(); } @@ -300,7 +327,7 @@ static void handle_key(XEvent* event) { uint32_t key = getkey(event); KeyBtnState = event->xkey.state; if (key == RUNE_ERR) return; - Config->handle_key(KeyBtnState, key); + Config.handle_key(KeyBtnState, key); } static void handle_mouse(XEvent* e) { @@ -309,20 +336,20 @@ static void handle_mouse(XEvent* e) { int y = e->xbutton.y; if (e->type == MotionNotify) { - Config->mouse_drag(KeyBtnState, x, y); + Config.mouse_drag(KeyBtnState, x, y); } else { if (e->type == ButtonRelease) KeyBtnState &= ~(1 << (e->xbutton.button + 7)); else KeyBtnState |= (1 << (e->xbutton.button + 7)); - Config->mouse_btn(e->xbutton.button, (e->type == ButtonPress), x, y); + Config.mouse_btn(e->xbutton.button, (e->type == ButtonPress), x, y); } } static void set_focus(bool focused) { if (X.xic) (focused ? XSetICFocus : XUnsetICFocus)(X.xic); - Config->set_focus(focused); + Config.set_focus(focused); } void x11_handle_event(XEvent* e) { @@ -337,10 +364,9 @@ void x11_handle_event(XEvent* e) { case SelectionClear: selclear(e); break; case SelectionNotify: selnotify(e); break; case SelectionRequest: selrequest(e); break; - case PropertyNotify: propnotify(e); break; case ClientMessage: if (e->xclient.data.l[0] == wmDeleteMessage) - Config->shutdown(); + Config.shutdown(); break; case ConfigureNotify: // Resize the window if (e->xconfigure.width != X.width || e->xconfigure.height != X.height) { @@ -368,15 +394,6 @@ void x11_events_take(void) { } } -void x11_mouse_set(int x, int y) { - XWarpPointer(X.display, X.self, X.self, 0, 0, X.width, X.height, x, y); -} - -void x11_mouse_get(int* ptrx, int* ptry) { - Window xw; int x; unsigned int ux; - XQueryPointer(X.display, X.self, &xw, &xw, &x, &x, ptrx, ptry, &ux); -} - XFont x11_font_load(char* name) { struct XFont* font = calloc(1, sizeof(struct XFont)); /* init the library and the base font pattern */ @@ -589,6 +606,7 @@ bool x11_sel_get(int selid, void(*cbfn)(char*)) { } else if (owner != None){ sel->callback = cbfn; XConvertSelection(X.display, sel->atom, SelTarget, sel->atom, X.self, CurrentTime); + } return true; } @@ -605,14 +623,6 @@ bool x11_sel_set(int selid, char* str) { } } -/* Tide Server Communication and Property Handling - *****************************************************************************/ -static void propnotify(XEvent* evnt) { - XPropertyEvent* ev = (XPropertyEvent*)evnt; - if (ev->atom == XInternAtom(X.display, "TIDE_COMM", False)) - Config->cmd_received(x11_prop_get("TIDE_COMM")); -} - static char* readprop(Display* disp, Window win, Atom prop) { Atom rtype; unsigned long format = 0, nitems = 0, nleft = 0, nread = 0; @@ -622,11 +632,363 @@ static char* readprop(Display* disp, Window win, Atom prop) { return (char*)data; } -void x11_prop_set(char* name, char* val) { - Atom prop = XInternAtom(X.display, name, False); - XChangeProperty(X.display, X.self, prop, XA_STRING, 8, PropModeReplace, (unsigned char*)val, strlen(val)+1); +/******************************************************************************/ + +static void win_init(void (*errfn)(char*)) { + for (int i = 0; i < SCROLL; i++) + view_init(&(Regions[i].view), NULL, errfn); + x11_init(&Config); + CurrFont = x11_font_load(FontString); + Regions[SCROLL].clrnor = Colors[ClrScrollNor]; + Regions[TAGS].clrnor = Colors[ClrTagsNor]; + Regions[TAGS].clrsel = Colors[ClrTagsSel]; + Regions[TAGS].clrcsr = Colors[ClrTagsCsr]; + Regions[EDIT].clrnor = Colors[ClrEditNor]; + Regions[EDIT].clrsel = Colors[ClrEditSel]; + Regions[EDIT].clrcsr = Colors[ClrEditCsr]; +} + +void win_window(char* name, bool isdialog, void (*errfn)(char*)) { + win_init(errfn); + if (isdialog) + x11_dialog(name, WinWidth, WinHeight); + else + x11_window(name, WinWidth, WinHeight); +} + +static void win_update(int xfd, void* data) { + if (xfd < 0) return; + if (x11_events_queued()) + x11_events_take(); + x11_flush(); +} + +void win_load(char* path) { + View* view = win_view(EDIT); + view_init(view, path, view->buffer.errfn); + path = view->buffer.path; +} + +void win_save(char* path) { + View* view = win_view(EDIT); + if (!path) path = view->buffer.path; + if (!path) return; + path = stringdup(path); + free(view->buffer.path); + view->buffer.path = path; + buf_save(&(view->buffer)); +} + +void win_loop(void) { + x11_show(); + while (x11_running()) { + bool pending = job_poll(ConnectionNumber(X.display), Timeout); + int nevents = x11_events_queued(); + if (pending || nevents) { + x11_events_take(); + if (x11_running()) + x11_flip(); + } + x11_flush(); + } + x11_finish(); +} + +void win_settext(WinRegion id, char* text) { + View* view = win_view(id); + view->buffer.gapstart = view->buffer.bufstart; + view->buffer.gapend = view->buffer.bufend; + view->selection = (Sel){0,0,0}; + view_putstr(view, text); + view_selprev(view); // clear the selection + buf_logclear(&(view->buffer)); +} + +void win_setruler(size_t ruler) { + Ruler = ruler; } -char* x11_prop_get(char* name) { - return readprop(X.display, X.self, XInternAtom(X.display, name, False)); +Rune win_getkey(void) { + return LastKey; +} + +void win_setkeys(KeyBinding* bindings, void (*inputfn)(Rune)) { + Keys = bindings; + InputFunc = inputfn; +} + +bool win_btnpressed(int btn) { + int btnmask = (1 << (btn + 7)); + return ((x11_keybtnstate() & btnmask) == btnmask); +} + +WinRegion win_getregion(void) { + return Focused; +} + +bool win_setregion(WinRegion id) { + bool changed = true; + if (Focused != id && (id == TAGS || id == EDIT)) + changed = true, Focused = id; + return changed; +} + +View* win_view(WinRegion id) { + if (id == FOCUSED) id = Focused; + return &(Regions[id].view); +} + +Buf* win_buf(WinRegion id) { + if (id == FOCUSED) id = Focused; + return &(Regions[id].view.buffer); +} + +Sel* win_sel(WinRegion id) { + if (id == FOCUSED) id = Focused; + return &(Regions[id].view.selection); +} + +void win_setscroll(double offset, double visible) { + ScrollOffset = offset; + ScrollVisible = visible; +} + +static void layout(int width, int height) { + size_t fheight = x11_font_height(CurrFont); + size_t fwidth = x11_font_width(CurrFont); + View* tagview = win_view(TAGS); + View* editview = win_view(EDIT); + + /* update the text views and region positions and sizes */ + for (int i = 0; i < SCROLL; i++) { + Regions[i].x = 2; + Regions[i].y = 2; + Regions[i].csrx = SIZE_MAX; + Regions[i].csry = SIZE_MAX; + Regions[i].width = (width - 4); + Regions[i].height = fheight; + } + + /* Place the tag region relative to status */ + size_t maxtagrows = ((height - Regions[TAGS].y - 5) / 4) / fheight; + size_t tagcols = Regions[TAGS].width / fwidth; + size_t tagrows = view_limitrows(tagview, maxtagrows, tagcols); + Regions[TAGS].height = tagrows * fheight; + view_resize(tagview, tagrows, tagcols); + + /* Place the scroll region relative to tags */ + Regions[SCROLL].x = 0; + Regions[SCROLL].y = 5 + Regions[TAGS].y + Regions[TAGS].height; + Regions[SCROLL].height = (height - Regions[EDIT].y - 5); + Regions[SCROLL].width = 5 + fwidth; + + /* Place the edit region relative to tags */ + Regions[EDIT].x = 3 + Regions[SCROLL].width; + Regions[EDIT].y = 5 + Regions[TAGS].y + Regions[TAGS].height; + Regions[EDIT].height = (height - Regions[EDIT].y - 5); + Regions[EDIT].width = width - Regions[SCROLL].width - 5; + view_resize(editview, Regions[EDIT].height / fheight, Regions[EDIT].width / fwidth); +} + +static void onredraw(int width, int height) { + size_t fheight = x11_font_height(CurrFont); + size_t fwidth = x11_font_width(CurrFont); + + layout(width, height); + onupdate(); // Let the user program update the status and other content + int clrtagnor = (Regions[TAGS].clrnor.bg << 8 | Regions[TAGS].clrnor.fg); + int clrtagsel = (Regions[TAGS].clrsel.bg << 8 | Regions[TAGS].clrsel.fg); + view_update(win_view(TAGS), clrtagnor, clrtagsel, &(Regions[TAGS].csrx), &(Regions[TAGS].csry)); + int clreditnor = (Regions[EDIT].clrnor.bg << 8 | Regions[EDIT].clrnor.fg); + int clreditsel = (Regions[EDIT].clrsel.bg << 8 | Regions[EDIT].clrsel.fg); + view_update(win_view(EDIT), clreditnor, clreditsel, &(Regions[EDIT].csrx), &(Regions[EDIT].csry)); + onlayout(); // Let the user program update the scroll bar + + int clr_hbor = Colors[ClrBorders].bg; + int clr_vbor = Colors[ClrBorders].bg; + CPair clr_scroll = Colors[ClrScrollNor]; + + for (int i = 0; i < SCROLL; i++) { + View* view = win_view(i); + x11_draw_rect(Regions[i].clrnor.bg, 0, Regions[i].y - 3, width, Regions[i].height + 8); + x11_draw_rect(clr_hbor, 0, Regions[i].y - 3, width, 1); + + if (i == EDIT) { + if (Ruler) + x11_draw_rect( Colors[ClrEditRul].fg, + ((Ruler+2) * fwidth), + Regions[i].y-2, + 1, + Regions[i].height+7 ); + } + + for (size_t line = 0, y = 0; y < view->nrows; y++) { + Row* row = view_getrow(view, y); + draw_glyphs(Regions[i].x, Regions[i].y + ((y+1) * fheight), row->cols, row->rlen, row->len); + } + } + + /* draw the scroll region */ + size_t thumbreg = (Regions[SCROLL].height - Regions[SCROLL].y + 9); + size_t thumboff = (size_t)((thumbreg * ScrollOffset) + (Regions[SCROLL].y - 2)); + size_t thumbsz = (size_t)(thumbreg * ScrollVisible); + if (thumbsz < 5) thumbsz = 5; + x11_draw_rect(clr_vbor, Regions[SCROLL].width, Regions[SCROLL].y - 2, 1, Regions[SCROLL].height); + x11_draw_rect(clr_scroll.bg, 0, Regions[SCROLL].y - 2, Regions[SCROLL].width, thumbreg); + x11_draw_rect(clr_scroll.fg, 0, thumboff, Regions[SCROLL].width, thumbsz); + + /* place the cursor on screen */ + if (Regions[Focused].csrx != SIZE_MAX && Regions[Focused].csry != SIZE_MAX) { + x11_draw_rect( + Regions[Focused].clrcsr.fg, + Regions[Focused].x + (Regions[Focused].csrx * fwidth), + Regions[Focused].y + (Regions[Focused].csry * fheight), + 1, fheight); + } +} + +static void oninput(int mods, Rune key) { + LastKey = key; + /* mask of modifiers we don't care about */ + mods = mods & (ModCtrl|ModShift|ModAlt); + /* handle the proper line endings */ + if (key == '\r') key = '\n'; + /* search for a key binding entry */ + uint32_t mkey = tolower(key); + for (KeyBinding* bind = Keys; bind && bind->key; bind++) { + bool match = (mkey == bind->key); + bool exact = (bind->mods == mods); + bool any = (bind->mods == ModAny); + bool oneplus = ((bind->mods == ModOneOrMore) && (mods & ModOneOrMore)); + if (match && (exact || oneplus || any)) { + bind->action(); + return; + } + } + + /* translate to crlf if needed */ + if (key == '\n' && win_view(FOCUSED)->buffer.crlf) + key = RUNE_CRLF; + + /* 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) { + if (InputFunc) + InputFunc(key); + else + view_insert(win_view(FOCUSED), true, key); + } +} + +static void scroll_actions(int btn, bool pressed, int x, int y) { + size_t row = (y-Regions[SCROLL].y) / x11_font_height(CurrFont); + switch (btn) { + case MouseLeft: + if (pressed) + view_scroll(win_view(EDIT), -row); + break; + case MouseMiddle: + if (pressed) + onscroll((double)(y - Regions[SCROLL].y) / + (double)(Regions[SCROLL].height - Regions[SCROLL].y)); + break; + case MouseRight: + if (pressed) + view_scroll(win_view(EDIT), +row); + break; + case MouseWheelUp: + view_scroll(win_view(EDIT), -ScrollBy); + break; + case MouseWheelDn: + view_scroll(win_view(EDIT), +ScrollBy); + break; + } +} + +static void onmousedrag(int state, int x, int y) { + if (x < Regions[Focused].x) x = Regions[Focused].x; + if (y < Regions[Focused].y) y = Regions[Focused].y; + size_t row = (y-Regions[Focused].y) / x11_font_height(CurrFont); + size_t col = (x-Regions[Focused].x) / x11_font_width(CurrFont); + if (win_btnpressed(MouseLeft)) + view_selext(win_view(Focused), row, col); +} + +static void onmousebtn(int btn, bool pressed, int x, int y) { + WinRegion id = getregion(x, y); + if (id == FOCUSED && x < Regions[Focused].x) + x = Regions[Focused].x, id = getregion(x, y); + size_t row = (y-Regions[id].y) / x11_font_height(CurrFont); + size_t col = (x-Regions[id].x) / x11_font_width(CurrFont); + + if (id == SCROLL) { + scroll_actions(btn, pressed, x, y); + } else { + switch(btn) { + case MouseLeft: onmouseleft(id, pressed, row, col); break; + case MouseMiddle: onmousemiddle(id, pressed, row, col); break; + case MouseRight: onmouseright(id, pressed, row, col); break; + case MouseWheelUp: onwheelup(id, pressed, row, col); break; + case MouseWheelDn: onwheeldn(id, pressed, row, col); break; + } + } +} + +static void onwheelup(WinRegion id, bool pressed, size_t row, size_t col) { + if (!pressed) return; + view_scroll(win_view(id), -ScrollBy); +} + +static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col) { + if (!pressed) return; + view_scroll(win_view(id), +ScrollBy); +} + +static void oncommand(char* cmd) { + size_t line = strtoul(cmd, NULL, 0); + if (line) { + View* view = win_view(EDIT); + win_setregion(EDIT); + view_setln(view, line); + view_eol(view, false); + view_selctx(view); + } +} + +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; + x11_font_getglyph(CurrFont, &(specs[numspecs]), glyphs[i].rune); + specs[numspecs].x = x; + specs[numspecs].y = y - x11_font_descent(CurrFont); + x += x11_font_width(CurrFont); + numspecs++; + i++; + /* skip over null chars which mark multi column runes */ + for (; i < ncols && !glyphs[i].rune; i++) + x += x11_font_width(CurrFont); + } + /* 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; + } +} + +static WinRegion 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 (WinRegion)i; + } + return NREGIONS; } diff --git a/tide.c b/tide.c index ed7307f..1be5eda 100644 --- a/tide.c +++ b/tide.c @@ -214,10 +214,6 @@ void onmouseright(WinRegion id, bool pressed, size_t row, size_t col) { SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1); free(SearchTerm); SearchTerm = view_fetch(win_view(id), row, col, risfile); - if (view_findstr(win_view(EDIT), SearchDir, SearchTerm)) { - win_setregion(EDIT); - win_warpptr(EDIT); - } } } @@ -245,10 +241,6 @@ static void search(void) { view_findstr(win_view(EDIT), SearchDir, str); free(SearchTerm); SearchTerm = str; - if (view_selsize(win_view(EDIT))) { - win_setregion(EDIT); - win_warpptr(EDIT); - } } static void execute(void) { -- 2.49.0