+++ /dev/null
-enum {
- MouseLeft = 1,
- MouseMiddle = 2,
- MouseRight = 3,
- MouseWheelUp = 4,
- MouseWheelDn = 5
-};
-
-typedef enum {
- STATUS = 0,
- TAGS = 1,
- EDIT = 2,
- SCROLL = 3,
- NREGIONS = 4,
- FOCUSED = 4
-} WinRegion;
-
-typedef struct {
- int mods;
- Rune key;
- void (*action)(void);
-} KeyBinding;
-
-typedef struct {
- size_t x;
- size_t y;
- size_t height;
- size_t width;
- size_t csrx;
- size_t csry;
- bool warp_ptr;
- View view;
-} Region;
-
-typedef void (*MouseFunc)(WinRegion id, size_t count, size_t row, size_t col);
-
-typedef struct {
- MouseFunc left;
- MouseFunc middle;
- MouseFunc right;
-} MouseConfig;
-
-void win_window(char* name, void (*errfn)(char*));
-void win_dialog(char* name, void (*errfn)(char*));
-void win_loop(void);
-void win_settext(WinRegion id, char* text);
-void win_setruler(size_t ruler);
-void win_setkeys(KeyBinding* bindings);
-void win_setmouse(MouseConfig* mconfig);
-void win_warpptr(WinRegion id);
-View* win_view(WinRegion id);
-Buf* win_buf(WinRegion id);
-Sel* win_sel(WinRegion id);
-bool win_btnpressed(int btn);
-WinRegion win_getregion(void);
-bool win_setregion(WinRegion id);
-void win_setscroll(double offset, double visible);
-
-/* These functions must be implemented by any appliation that wishes
- to use this module */
-void onshutdown(void);
-void onfocus(bool focused);
-void onupdate(void);
-void onlayout(void);
-void onscroll(double percent);
-void onmouseleft(WinRegion id, bool pressed, size_t row, size_t col);
-void onmousemiddle(WinRegion id, bool pressed, size_t row, size_t col);
-void onmouseright(WinRegion id, bool pressed, size_t row, size_t col);
-
+++ /dev/null
-typedef struct {
- void (*redraw)(int width, int height);
- void (*handle_key)(int mods, uint32_t rune);
- void (*shutdown)(void);
- void (*set_focus)(bool focus);
- void (*mouse_drag)(int state, int x, int y);
- void (*mouse_btn)(int state, bool pressed, int x, int y);
- uint32_t palette[16];
-} XConfig;
-
-typedef void* XFont;
-
-typedef struct {
- uint32_t attr; /* attributes applied to this cell */
- uint32_t rune; /* rune value for the cell */
-} XGlyph;
-
-typedef struct {
- void* font;
- uint32_t glyph;
- short x;
- short y;
-} XGlyphSpec;
-
-/* key definitions */
-enum Keys {
- /* Define some runes in the private use area of unicode to represent
- * special keys */
- KEY_F1 = (0xE000+0),
- KEY_F2 = (0xE000+1),
- KEY_F3 = (0xE000+2),
- KEY_F4 = (0xE000+3),
- KEY_F5 = (0xE000+4),
- KEY_F6 = (0xE000+5),
- KEY_F7 = (0xE000+6),
- KEY_F8 = (0xE000+7),
- KEY_F9 = (0xE000+8),
- KEY_F10 = (0xE000+9),
- KEY_F11 = (0xE000+10),
- KEY_F12 = (0xE000+11),
- KEY_INSERT = (0xE000+12),
- KEY_DELETE = (0xE000+13),
- KEY_HOME = (0xE000+14),
- KEY_END = (0xE000+15),
- KEY_PGUP = (0xE000+16),
- KEY_PGDN = (0xE000+17),
- KEY_UP = (0xE000+18),
- KEY_DOWN = (0xE000+19),
- KEY_RIGHT = (0xE000+20),
- KEY_LEFT = (0xE000+21),
-
- /* ASCII Control Characters */
- KEY_CTRL_TILDE = 0x00,
- KEY_CTRL_2 = 0x00,
- KEY_CTRL_A = 0x01,
- KEY_CTRL_B = 0x02,
- KEY_CTRL_C = 0x03,
- KEY_CTRL_D = 0x04,
- KEY_CTRL_E = 0x05,
- KEY_CTRL_F = 0x06,
- KEY_CTRL_G = 0x07,
- KEY_BACKSPACE = 0x08,
- KEY_CTRL_H = 0x08,
- KEY_TAB = 0x09,
- KEY_CTRL_I = 0x09,
- KEY_CTRL_J = 0x0A,
- KEY_CTRL_K = 0x0B,
- KEY_CTRL_L = 0x0C,
- KEY_ENTER = 0x0D,
- KEY_CTRL_M = 0x0D,
- KEY_CTRL_N = 0x0E,
- KEY_CTRL_O = 0x0F,
- KEY_CTRL_P = 0x10,
- KEY_CTRL_Q = 0x11,
- KEY_CTRL_R = 0x12,
- KEY_CTRL_S = 0x13,
- KEY_CTRL_T = 0x14,
- KEY_CTRL_U = 0x15,
- KEY_CTRL_V = 0x16,
- KEY_CTRL_W = 0x17,
- KEY_CTRL_X = 0x18,
- KEY_CTRL_Y = 0x19,
- KEY_CTRL_Z = 0x1A,
- KEY_ESCAPE = 0x1B,
- KEY_CTRL_LSQ_BRACKET = 0x1B,
- KEY_CTRL_3 = 0x1B,
- KEY_CTRL_4 = 0x1C,
- KEY_CTRL_BACKSLASH = 0x1C,
- KEY_CTRL_5 = 0x1D,
- KEY_CTRL_RSQ_BRACKET = 0x1D,
- KEY_CTRL_6 = 0x1E,
- KEY_CTRL_7 = 0x1F,
- KEY_CTRL_SLASH = 0x1F,
- KEY_CTRL_UNDERSCORE = 0x1F,
-};
-
-/* Key modifier masks */
-enum {
- ModNone = 0,
- ModShift = (1 << 0),
- ModCapsLock = (1 << 1),
- ModCtrl = (1 << 2),
- ModAlt = (1 << 3),
- ModNumLock = (1 << 4),
- ModScrollLock = (1 << 5),
- ModWindows = (1 << 6),
- ModAny = -1
-};
-
-/* Selection identifiers */
-enum {
- PRIMARY = 0,
- CLIPBOARD = 1
-};
-
-void x11_init(XConfig* cfg);
-void x11_deinit(void);
-int x11_keybtnstate(void);
-bool x11_keymodsset(int mask);
-void x11_window(char* name, int width, int height);
-void x11_dialog(char* name, int height, int width);
-void x11_show(void);
-bool x11_running(void);
-void x11_flip(void);
-void x11_flush(void);
-void x11_finish(void);
-
-int x11_events_queued(void);
-bool x11_events_await(unsigned int ms);
-void x11_events_take(void);
-
-void x11_mouse_get(int* x, int* y);
-void x11_mouse_set(int x, int y);
-
-XFont x11_font_load(char* name);
-size_t x11_font_height(XFont fnt);
-size_t x11_font_width(XFont fnt);
-size_t x11_font_descent(XFont fnt);
-void x11_font_getglyph(XFont font, XGlyphSpec* spec, uint32_t rune);
-size_t x11_font_getglyphs(XGlyphSpec* specs, const XGlyph* glyphs, int len, XFont font, int x, int y);
-
-void x11_draw_rect(int color, int x, int y, int width, int height);
-void x11_draw_utf8(XFont font, int fg, int bg, int x, int y, char* str);
-void x11_draw_glyphs(int fg, int bg, XGlyphSpec* glyphs, size_t nglyphs);
-void x11_draw_utf8(XFont font, int fg, int bg, int x, int y, char* str);
-
-bool x11_sel_get(int selid, void(*cbfn)(char*));
-bool x11_sel_set(int selid, char* str);
-
+++ /dev/null
-#include <stdc.h>
-#include <utf.h>
-#include <edit.h>
-#include <x11.h>
-#include <win.h>
-#include <ctype.h>
-
-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 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,
- .palette = COLOR_PALETTE
-};
-static WinRegion Focused = EDIT;
-static Region Regions[NREGIONS] = {0};
-KeyBinding* Keys = NULL;
-
-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(FONTNAME);
-}
-
-void win_window(char* name, void (*errfn)(char*)) {
- win_init(errfn);
- x11_window(name, Width, Height);
-}
-
-void win_dialog(char* name, void (*errfn)(char*)) {
- win_init(errfn);
- x11_dialog(name, Width, Height);
-}
-
-static bool update_focus(void) {
- 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;
-}
-
-void win_loop(void) {
- x11_show();
- x11_flip();
- while (x11_running()) {
- bool pending = x11_events_await(EventTimeout);
- 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;
-}
-
-void win_setkeys(KeyBinding* bindings) {
- Keys = bindings;
-}
-
-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* statview = win_view(STATUS);
- 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 status region */
- view_resize(statview, 1, Regions[STATUS].width / fwidth);
-
- /* Place the tag region relative to status */
- Regions[TAGS].y = 5 + Regions[STATUS].y + Regions[STATUS].height;
- size_t maxtagrows = ((height - Regions[TAGS].y - 5) / 4) / fheight;
- size_t tagcols = Regions[TAGS].width / fwidth;
- size_t tagrows = view_limitrows(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
- view_update(win_view(STATUS), &(Regions[STATUS].csrx), &(Regions[STATUS].csry));
- view_update(win_view(TAGS), &(Regions[TAGS].csrx), &(Regions[TAGS].csry));
- view_update(win_view(EDIT), &(Regions[EDIT].csrx), &(Regions[EDIT].csry));
- onlayout(); // Let the user program update the scroll bar
-
- for (int i = 0; i < SCROLL; i++) {
- View* view = win_view(i);
- x11_draw_rect((i == TAGS ? CLR_BASE02 : CLR_BASE03),
- 0, Regions[i].y - 3, width, Regions[i].height + 8);
- x11_draw_rect(CLR_BASE01, 0, Regions[i].y - 3, width, 1);
- if ((i == EDIT) && (Ruler != 0))
- x11_draw_rect(CLR_BASE02, (Ruler+2) * fwidth, Regions[i].y-2, 1, Regions[i].height+7);
- for (size_t 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_BASE01, Regions[SCROLL].width, Regions[SCROLL].y - 2, 1, Regions[SCROLL].height);
- x11_draw_rect(CLR_BASE00, 0, Regions[SCROLL].y - 2, Regions[SCROLL].width, thumbreg);
- x11_draw_rect(CLR_BASE03, 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(CLR_BASE3,
- 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) {
- /* mask of modifiers we don't care about */
- mods = mods & (ModCtrl|ModAlt|ModShift);
- /* 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++) {
- if ((mkey == bind->key) && (bind->mods == ModAny || bind->mods == mods)) {
- bind->action();
- return;
- }
- }
-
- /* 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 (key == '\n' && win_view(FOCUSED)->buffer.crlf)
- key = RUNE_CRLF;
- 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);
- size_t col = (x-Regions[SCROLL].x) / x11_font_width(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), -ScrollLines);
- break;
- case MouseWheelDn:
- view_scroll(win_view(EDIT), +ScrollLines);
- break;
- }
-}
-
-static void onmousedrag(int state, int x, int y) {
- WinRegion 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 == Focused && win_btnpressed(MouseLeft))
- view_selext(win_view(id), row, col);
-}
-
-static void onmousebtn(int btn, bool pressed, int x, int y) {
- WinRegion 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), -ScrollLines);
-}
-
-static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col) {
- if (!pressed) return;
- view_scroll(win_view(id), +ScrollLines);
-}
-
-static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols) {
- XGlyphSpec specs[rlen];
- size_t i = 0;
- while (rlen && i < ncols) {
- int numspecs = 0;
- uint32_t attr = glyphs[i].attr;
- while (i < ncols && glyphs[i].attr == attr) {
- x11_font_getglyph(Font, &(specs[numspecs]), glyphs[i].rune);
- specs[numspecs].x = x;
- specs[numspecs].y = y - x11_font_descent(Font);
- x += x11_font_width(Font);
- numspecs++;
- i++;
- /* skip over null chars which mark multi column runes */
- for (; i < ncols && !glyphs[i].rune; i++)
- x += x11_font_width(Font);
- }
- /* Draw the glyphs with the proper colors */
- uint8_t bg = attr >> 8;
- uint8_t fg = attr & 0xFF;
- x11_draw_glyphs(fg, bg, specs, numspecs);
- rlen -= numspecs;
- }
-}
-
-static 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;
-}
+++ /dev/null
-#include <X11/Xlib.h>
-#include <X11/Xatom.h>
-#include <X11/Xft/Xft.h>
-#include <stdc.h>
-#include <x11.h>
-#include <utf.h>
-#include <edit.h>
-#include <locale.h>
-#include <sys/time.h>
-#include <sys/types.h>
-
-static struct XSel* selfetch(Atom atom);
-static void selclear(XEvent* evnt);
-static void selnotify(XEvent* evnt);
-static void selrequest(XEvent* evnt);
-
-struct XFont {
- struct {
- int height;
- int width;
- int ascent;
- int descent;
- XftFont* match;
- FcFontSet* set;
- FcPattern* pattern;
- } base;
- struct {
- XftFont* font;
- uint32_t unicodep;
- } cache[FontCacheSize];
- int ncached;
-};
-
-static bool Running = true;
-static struct {
- Window root;
- Display* display;
- Visual* visual;
- Colormap colormap;
- unsigned depth;
- int screen;
- /* assume a single window for now. these are it's attributes */
- Window window;
- XftDraw* xft;
- Pixmap pixmap;
- int width;
- int height;
- XIC xic;
- XIM xim;
- GC gc;
-} X;
-static XConfig* Config;
-static int KeyBtnState;
-static Atom SelTarget;
-static struct XSel {
- char* name;
- Atom atom;
- char* text;
- void (*callback)(char*);
-} Selections[] = {
- { .name = "PRIMARY" },
- { .name = "CLIPBOARD" },
-};
-
-static void xftcolor(XftColor* xc, uint32_t c) {
- xc->color.alpha = 0xFF | ((c & 0xFF000000) >> 16);
- xc->color.red = 0xFF | ((c & 0x00FF0000) >> 8);
- xc->color.green = 0xFF | ((c & 0x0000FF00));
- xc->color.blue = 0xFF | ((c & 0x000000FF) << 8);
- XftColorAllocValue(X.display, X.visual, X.colormap, &(xc->color), xc);
-}
-
-void x11_deinit(void) {
- Running = false;
-}
-
-void x11_init(XConfig* cfg) {
- atexit(x11_deinit);
- signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal
- 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);
- 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);
- /* 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);
-}
-
-int x11_keybtnstate(void) {
- return KeyBtnState;
-}
-
-bool x11_keymodsset(int mask) {
- return ((KeyBtnState & mask) == mask);
-}
-
-void x11_window(char* name, int width, int height) {
- /* create the main window */
- X.width = width ;
- X.height = height;
- XWindowAttributes wa;
- XGetWindowAttributes(X.display, X.root, &wa);
- X.window = XCreateSimpleWindow(X.display, X.root,
- (wa.width - X.width) / 2,
- (wa.height - X.height) /2,
- X.width,
- X.height,
- 0, X.depth,
- Config->palette[0]);
-
- /* register interest in the delete window message */
- Atom wmDeleteMessage = XInternAtom(X.display, "WM_DELETE_WINDOW", False);
- XSetWMProtocols(X.display, X.window, &wmDeleteMessage, 1);
-
- /* setup window attributes and events */
- XSetWindowAttributes swa;
- swa.backing_store = WhenMapped;
- swa.bit_gravity = NorthWestGravity;
- XChangeWindowAttributes(X.display, X.window, CWBackingStore|CWBitGravity, &swa);
- XStoreName(X.display, X.window, name);
- XSelectInput(X.display, X.window,
- StructureNotifyMask
- | ButtonPressMask
- | ButtonReleaseMask
- | ButtonMotionMask
- | KeyPressMask
- | FocusChangeMask
- );
-
- /* set input methods */
- if ((X.xim = XOpenIM(X.display, 0, 0, 0)))
- X.xic = XCreateIC(X.xim, XNInputStyle, XIMPreeditNothing|XIMStatusNothing, XNClientWindow, X.window, XNFocusWindow, X.window, NULL);
-
- /* initialize pixmap and drawing context */
- X.pixmap = XCreatePixmap(X.display, X.window, width, 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);
- gcv.graphics_exposures = False;
- X.gc = XCreateGC(X.display, X.window, GCForeground|GCGraphicsExposures, &gcv);
-}
-
-void x11_dialog(char* name, int height, int width) {
- x11_window(name, height, width);
- Atom WindowType = XInternAtom(X.display, "_NET_WM_WINDOW_TYPE", False);
- Atom DialogType = XInternAtom(X.display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
- XChangeProperty(X.display, X.window, WindowType, XA_ATOM, 32, PropModeReplace, (unsigned char*)&DialogType, 1);
-}
-
-void x11_show(void) {
- /* simulate an initial resize and map the window */
- XConfigureEvent ce;
- ce.type = ConfigureNotify;
- ce.width = X.width;
- ce.height = X.height;
- XSendEvent(X.display, X.window, False, StructureNotifyMask, (XEvent *)&ce);
- XMapWindow(X.display, X.window);
-}
-
-bool x11_running(void) {
- return Running;
-}
-
-void x11_flip(void) {
- Config->redraw(X.width, X.height);
- XCopyArea(X.display, X.pixmap, X.window, X.gc, 0, 0, X.width, X.height, 0, 0);
- x11_flush();
-}
-
-void x11_flush(void) {
- XFlush(X.display);
-}
-
-void x11_finish(void) {
- XCloseDisplay(X.display);
- /* we're exiting now. If we own the clipboard, make sure it persists */
- if (Selections[CLIPBOARD].text)
- cmdwrite((char*[]){ "xcpd", NULL }, Selections[CLIPBOARD].text, NULL);
-}
-
-/******************************************************************************/
-
-static uint32_t special_keys(uint32_t key) {
- switch (key) {
- case XK_F1: return KEY_F1;
- case XK_F2: return KEY_F2;
- case XK_F3: return KEY_F3;
- case XK_F4: return KEY_F4;
- case XK_F5: return KEY_F5;
- case XK_F6: return KEY_F6;
- case XK_F7: return KEY_F7;
- case XK_F8: return KEY_F8;
- case XK_F9: return KEY_F9;
- case XK_F10: return KEY_F10;
- case XK_F11: return KEY_F11;
- case XK_F12: return KEY_F12;
- case XK_Insert: return KEY_INSERT;
- case XK_Delete: return KEY_DELETE;
- case XK_Home: return KEY_HOME;
- case XK_End: return KEY_END;
- case XK_Page_Up: return KEY_PGUP;
- case XK_Page_Down: return KEY_PGDN;
- case XK_Up: return KEY_UP;
- case XK_Down: return KEY_DOWN;
- case XK_Left: return KEY_LEFT;
- case XK_Right: return KEY_RIGHT;
- case XK_Escape: return KEY_ESCAPE;
- case XK_BackSpace: return '\b';
- case XK_Tab: return '\t';
- case XK_Return: return '\r';
- case XK_Linefeed: return '\n';
-
- /* modifiers should not trigger key presses */
- case XK_Scroll_Lock:
- case XK_Shift_L:
- case XK_Shift_R:
- case XK_Control_L:
- case XK_Control_R:
- case XK_Caps_Lock:
- case XK_Shift_Lock:
- case XK_Meta_L:
- case XK_Meta_R:
- case XK_Alt_L:
- case XK_Alt_R:
- case XK_Super_L:
- case XK_Super_R:
- case XK_Hyper_L:
- case XK_Hyper_R:
- return RUNE_ERR;
-
- /* if it ain't special, don't touch it */
- default:
- return key;
- }
-}
-
-static uint32_t getkey(XEvent* e) {
- uint32_t rune = RUNE_ERR;
- size_t len = 0;
- char buf[8];
- KeySym key;
- Status status;
- /* Read the key string */
- if (X.xic)
- len = Xutf8LookupString(X.xic, &(e->xkey), buf, sizeof(buf), &key, &status);
- else
- len = XLookupString(&(e->xkey), buf, sizeof(buf), &key, 0);
- /* if it's ascii, just return it */
- if (key >= 0x20 && key <= 0x7F)
- return (uint32_t)key;
- /* decode it */
- if (len > 0) {
- len = 0;
- for (int i = 0; i < 8 && !utf8decode(&rune, &len, buf[i]); i++);
- }
- /* translate special key codes into unicode codepoints */
- rune = special_keys(key);
- return rune;
-}
-
-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);
-}
-
-static void handle_mouse(XEvent* e) {
- KeyBtnState = e->xbutton.state;
- int x = e->xbutton.x;
- int y = e->xbutton.y;
-
- if (e->type == MotionNotify) {
- 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);
- }
-}
-
-static void set_focus(bool focused) {
- if (focused) {
- if (X.xic) XSetICFocus(X.xic);
- } else {
- if (X.xic) XUnsetICFocus(X.xic);
- }
- Config->set_focus(focused);
-}
-
-void x11_handle_event(XEvent* e) {
- Atom wmDeleteMessage = XInternAtom(X.display, "WM_DELETE_WINDOW", False);
- switch (e->type) {
- case FocusIn: set_focus(true); break;
- case FocusOut: set_focus(false); break;
- case KeyPress: handle_key(e); break;
- case ButtonRelease: handle_mouse(e); break;
- case ButtonPress: handle_mouse(e); break;
- case MotionNotify: handle_mouse(e); break;
- case SelectionClear: selclear(e); break;
- case SelectionNotify: selnotify(e); break;
- case SelectionRequest: selrequest(e); break;
- case ClientMessage:
- if (e->xclient.data.l[0] == wmDeleteMessage)
- Config->shutdown();
- break;
- case ConfigureNotify: // Resize the window
- if (e->xconfigure.width != X.width || e->xconfigure.height != X.height) {
- X.width = e->xconfigure.width;
- X.height = e->xconfigure.height;
- X.pixmap = XCreatePixmap(X.display, X.window, X.width, X.height, X.depth);
- X.xft = XftDrawCreate(X.display, X.pixmap, X.visual, X.colormap);
- }
- break;
- }
-}
-
-int x11_events_queued(void) {
- return XEventsQueued(X.display, QueuedAfterFlush);
-}
-
-bool x11_events_await(unsigned int ms) {
- fd_set fds;
- int xfd = ConnectionNumber(X.display), redraw = 1;
- /* configure for 100ms timeout */
- struct timeval tv = { .tv_usec = ms * 1000 };
- FD_ZERO(&fds);
- FD_SET(xfd, &fds);
- return (select(xfd+1, &fds, NULL, NULL, &tv) > 0);
-}
-
-void x11_events_take(void) {
- XEvent e;
- int nevents;
- XGetMotionEvents(X.display, X.window, CurrentTime, CurrentTime, &nevents);
- while (XPending(X.display)) {
- XNextEvent(X.display, &e);
- if (!XFilterEvent(&e, None))
- x11_handle_event(&e);
- }
-}
-
-void x11_mouse_set(int x, int y) {
- XWarpPointer(X.display, X.window, X.window, 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.window, &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 */
- if (!FcInit())
- die("Could not init fontconfig.\n");
- FcPattern* pattern = FcNameParse((FcChar8 *)name);
- if (!pattern)
- die("can't open font %s\n", name);
-
- /* load the base font */
- FcResult result;
- FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result);
- if (!match || !(font->base.match = XftFontOpenPattern(X.display, match)))
- die("could not load default font: %s", name);
-
- /* get base font extents */
- XGlyphInfo extents;
- const FcChar8 ascii[] =
- " !\"#$%&'()*+,-./0123456789:;<=>?"
- "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
- "`abcdefghijklmnopqrstuvwxyz{|}~";
- XftTextExtentsUtf8(X.display, font->base.match, ascii, sizeof(ascii), &extents);
- font->base.set = NULL;
- font->base.pattern = FcPatternDuplicate(pattern);
- font->base.ascent = font->base.match->ascent;
- font->base.descent = font->base.match->descent;
- font->base.height = font->base.ascent + font->base.descent;
- font->base.width = ((extents.xOff + (sizeof(ascii) - 1)) / sizeof(ascii));
- FcPatternDestroy(pattern);
- return font;
-}
-
-size_t x11_font_height(XFont fnt) {
- struct XFont* font = fnt;
- return font->base.height;
-}
-
-size_t x11_font_width(XFont fnt) {
- struct XFont* font = fnt;
- return font->base.width;
-}
-
-size_t x11_font_descent(XFont fnt) {
- struct XFont* font = fnt;
- return font->base.descent;
-}
-
-void x11_draw_rect(int color, int x, int y, int width, int height) {
- XftColor clr;
- xftcolor(&clr, Config->palette[color]);
- XftDrawRect(X.xft, &clr, x, y, width, height);
- XftColorFree(X.display, X.visual, X.colormap, &clr);
-}
-
-void x11_font_getglyph(XFont fnt, XGlyphSpec* spec, uint32_t rune) {
- struct XFont* font = fnt;
- /* if the rune is in the base font, set it and return */
- FT_UInt glyphidx = XftCharIndex(X.display, font->base.match, rune);
- if (glyphidx) {
- spec->font = font->base.match;
- spec->glyph = glyphidx;
- return;
- }
- /* Otherwise check the cache */
- for (int f = 0; f < font->ncached; f++) {
- glyphidx = XftCharIndex(X.display, font->cache[f].font, rune);
- /* Fond a suitable font or found a default font */
- if (glyphidx || (!glyphidx && font->cache[f].unicodep == rune)) {
- spec->font = font->cache[f].font;
- spec->glyph = glyphidx;
- return;
- }
- }
- /* if all other options fail, ask fontconfig for a suitable font */
- FcResult fcres;
- if (!font->base.set)
- font->base.set = FcFontSort(0, font->base.pattern, 1, 0, &fcres);
- FcFontSet* fcsets[] = { font->base.set };
- FcPattern* fcpattern = FcPatternDuplicate(font->base.pattern);
- FcCharSet* fccharset = FcCharSetCreate();
- FcCharSetAddChar(fccharset, rune);
- FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
- FcPatternAddBool(fcpattern, FC_SCALABLE, 1);
- FcConfigSubstitute(0, fcpattern, FcMatchPattern);
- FcDefaultSubstitute(fcpattern);
- FcPattern* fontmatch = FcFontSetMatch(0, fcsets, 1, fcpattern, &fcres);
- /* add the font to the cache and use it */
- if (font->ncached >= FontCacheSize) {
- font->ncached = FontCacheSize - 1;
- XftFontClose(X.display, font->cache[font->ncached].font);
- }
- font->cache[font->ncached].font = XftFontOpenPattern(X.display, fontmatch);
- font->cache[font->ncached].unicodep = rune;
- spec->glyph = XftCharIndex(X.display, font->cache[font->ncached].font, rune);
- spec->font = font->cache[font->ncached].font;
- font->ncached++;
- FcPatternDestroy(fcpattern);
- FcCharSetDestroy(fccharset);
-}
-
-size_t x11_font_getglyphs(XGlyphSpec* specs, const XGlyph* glyphs, int len, XFont fnt, int x, int y) {
- struct XFont* font = fnt;
- int winx = x, winy = y;
- size_t numspecs = 0;
- for (int i = 0, xp = winx, yp = winy + font->base.ascent; i < len;) {
- x11_font_getglyph(font, &(specs[numspecs]), glyphs[i].rune);
- specs[numspecs].x = xp;
- specs[numspecs].y = yp;
- xp += font->base.width;
- numspecs++;
- i++;
- /* skip over null chars which mark multi column runes */
- for (; i < len && !glyphs[i].rune; i++)
- xp += font->base.width;
- }
- return numspecs;
-}
-
-void x11_draw_glyphs(int fg, int bg, XGlyphSpec* specs, size_t nspecs) {
- if (!nspecs) return;
- XftFont* font = specs[0].font;
- XftColor fgc, bgc;
- if (bg > 0) {
- XGlyphInfo extent;
- XftTextExtentsUtf8(X.display, font, (const FcChar8*)"0", 1, &extent);
- int w = extent.xOff;
- int h = (font->height - font->descent) + LineSpacing;
- xftcolor(&bgc, Config->palette[bg]);
- size_t width = specs[nspecs-1].x - specs[0].x + w;
- x11_draw_rect(bg, specs[0].x, specs[0].y - h, width, font->height + LineSpacing);
- XftColorFree(X.display, X.visual, X.colormap, &bgc);
- }
- xftcolor(&fgc, Config->palette[fg]);
- XftDrawGlyphFontSpec(X.xft, &fgc, (XftGlyphFontSpec*)specs, nspecs);
- XftColorFree(X.display, X.visual, X.colormap, &fgc);
-}
-
-void x11_draw_utf8(XFont fnt, int fg, int bg, int x, int y, char* str) {
- struct XFont* font = fnt;
- static XftGlyphFontSpec specs[256];
- size_t nspecs = 0;
- while (*str && nspecs < 256) {
- x11_font_getglyph(font, (XGlyphSpec*)&(specs[nspecs]), *str);
- specs[nspecs].x = x;
- specs[nspecs].y = y;
- x += font->base.width;
- nspecs++;
- str++;
- }
- x11_draw_glyphs(fg, bg, (XGlyphSpec*)specs, nspecs);
-}
-
-/* Selection Handling
- *****************************************************************************/
-
-static char* readprop(Display* disp, Window win, Atom prop) {
- Atom type;
- int format;
- unsigned long nitems, nleft;
- unsigned char* ret = NULL;
- int nread = 1024;
-
- // Read the property in progressively larger chunks until the entire
- // property has been read (nleft == 0)
- do {
- if (ret) XFree(ret);
- XGetWindowProperty(disp, win, prop, 0, nread, False, AnyPropertyType,
- &type, &format, &nitems, &nleft,
- &ret);
- nread *= 2;
- } while (nleft != 0);
-
- return (char*)ret;
-}
-
-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 selclear(XEvent* evnt) {
- struct XSel* sel = selfetch(evnt->xselectionrequest.selection);
- if (!sel) return;
- free(sel->text);
- sel->text = NULL;
-}
-
-static void selnotify(XEvent* evnt) {
- /* bail if the selection cannot be converted */
- if (evnt->xselection.property == None)
- return;
- struct XSel* sel = selfetch( evnt->xselection.selection );
- char* propdata = readprop(X.display, X.window, sel->atom);
- if (evnt->xselection.target == SelTarget) {
- void(*cbfn)(char*) = sel->callback;
- sel->callback = NULL;
- cbfn(propdata);
- }
- /* cleanup */
- if (propdata) XFree(propdata);
-}
-
-static void selrequest(XEvent* evnt) {
- XEvent s;
- struct XSel* sel = selfetch( evnt->xselectionrequest.selection );
- s.xselection.type = SelectionNotify;
- s.xselection.property = evnt->xselectionrequest.property;
- s.xselection.requestor = evnt->xselectionrequest.requestor;
- s.xselection.selection = evnt->xselectionrequest.selection;
- s.xselection.target = evnt->xselectionrequest.target;
- s.xselection.time = evnt->xselectionrequest.time;
-
- Atom target = evnt->xselectionrequest.target;
- Atom xatargets = XInternAtom(X.display, "TARGETS", 0);
- Atom xastring = XInternAtom(X.display, "STRING", 0);
- if (target == xatargets) {
- /* respond with the supported type */
- XChangeProperty(
- X.display,
- s.xselection.requestor,
- s.xselection.property,
- XA_ATOM, 32, PropModeReplace,
- (unsigned char*)&SelTarget, 1);
- } else if (target == SelTarget || target == xastring) {
- XChangeProperty(
- X.display,
- s.xselection.requestor,
- s.xselection.property,
- SelTarget, 8, PropModeReplace,
- (unsigned char*)sel->text, strlen(sel->text));
- }
- XSendEvent(X.display, s.xselection.requestor, True, 0, &s);
-}
-
-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.window) {
- cbfn(sel->text);
- } else if (owner != None){
- sel->callback = cbfn;
- XConvertSelection(X.display, sel->atom, SelTarget, sel->atom, X.window, CurrentTime);
- }
- 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.window, CurrentTime);
- return true;
- }
-}
+++ /dev/null
-#include <stdc.h>
-#include <x11.h>
-#include <utf.h>
-#include <edit.h>
-#include <ctype.h>
-#include <win.h>
-#include <shortcuts.h>
-
-typedef struct {
- char* tag;
- union {
- void (*noarg)(void);
- void (*arg)(char* arg);
- } action;
-} Tag;
-
-/* The shell: Filled in with $SHELL. Used to execute commands */
-static char* ShellCmd[] = { NULL, "-c", NULL, NULL };
-static char* SedCmd[] = { "sed", "-e", NULL, NULL };
-static char* PickFileCmd[] = { "xfilepick", ".", NULL };
-static char* PickTagCmd[] = { "xtagpick", NULL, "tags", NULL, NULL };
-static char* OpenCmd[] = { "xedit", NULL, NULL };
-static Tag Builtins[];
-static int SearchDir = DOWN;
-static char* SearchTerm = NULL;
-
-/* Tag/Cmd Execution
- ******************************************************************************/
-static Tag* tag_lookup(char* cmd) {
- size_t len = 0;
- Tag* tags = Builtins;
- for (char* tag = cmd; *tag && !isspace(*tag); tag++, len++);
- while (tags->tag) {
- if (!strncmp(tags->tag, cmd, len))
- return tags;
- tags++;
- }
- return NULL;
-}
-
-static void tag_exec(Tag* tag, char* arg) {
- /* if we didnt get an arg, find one in the selection */
- if (!arg) arg = view_getstr(win_view(TAGS), NULL);
- if (!arg) arg = view_getstr(win_view(EDIT), NULL);
- /* execute the tag handler */
- tag->action.arg(arg);
- free(arg);
-}
-
-static void cmd_exec(char* cmd) {
- char op = '\0';
- if (*cmd == ':' || *cmd == '!' || *cmd == '<' || *cmd == '|' || *cmd == '>')
- op = *cmd, cmd++;
- ShellCmd[2] = cmd;
- /* execute the command */
- char *input = NULL, *output = NULL, *error = NULL;
- WinRegion dest = EDIT;
- if (op && op != '<' && op != '!' && 0 == view_selsize(win_view(EDIT)))
- win_view(EDIT)->selection = (Sel){ .beg = 0, .end = buf_end(win_buf(EDIT)) };
- input = view_getstr(win_view(EDIT), NULL);
-
- if (op == '!') {
- cmdrun(ShellCmd, NULL);
- } else if (op == '>') {
- dest = TAGS;
- output = cmdwriteread(ShellCmd, input, &error);
- } else if (op == '|') {
- output = cmdwriteread(ShellCmd, input, &error);
- } else if (op == ':') {
- SedCmd[2] = cmd;
- output = cmdwriteread(SedCmd, input, &error);
- } else {
- if (op != '<') dest = win_getregion();
- output = cmdread(ShellCmd, &error);
- }
-
- if (error)
- view_append(win_view(TAGS), chomp(error));
-
- if (output) {
- if (op == '>')
- view_append(win_view(dest), chomp(output));
- else
- view_putstr(win_view(dest), output);
- win_setregion(dest);
- }
- /* cleanup */
- free(input);
- free(output);
- free(error);
-}
-
-static void exec(char* cmd) {
- /* skip leading space */
- for (; *cmd && isspace(*cmd); cmd++);
- if (!*cmd) return;
- /* see if it matches a builtin tag */
- Tag* tag = tag_lookup(cmd);
- if (tag) {
- while (*cmd && !isspace(*cmd++));
- tag_exec(tag, (*cmd ? stringdup(cmd) : NULL));
- } else {
- cmd_exec(cmd);
- }
-}
-
-/* Action Callbacks
- ******************************************************************************/
-static void onerror(char* msg) {
- view_append(win_view(TAGS), msg);
-}
-
-static void trim_whitespace(void) {
- Buf* buf = win_buf(EDIT);
- if (TrimOnSave && buf_end(buf) > 0) {
- View* view = win_view(EDIT);
- unsigned off = 0, prev = 1;
- /* loop through the buffer till we hit the end or we stop advancing */
- while (off < buf_end(buf) && prev != off) {
- off = buf_eol(buf, off);
- Rune r = buf_get(buf, off-1);
- for (; (r == ' ' || r == '\t'); r = buf_get(buf, off-1)) {
- if (off <= view->selection.beg) {
- view->selection.end--;
- view->selection.beg--;
- }
- off = buf_delete(buf, off-1, off);
- }
- /* make sure we keep advancing */
- prev = off;
- off = buf_byline(buf, off, +1);
- }
- }
-}
-
-static void quit(void) {
- static uint64_t before = 0;
- uint64_t now = getmillis();
- if (!win_buf(EDIT)->modified || (now-before) <= 250) {
- #ifndef TEST
- x11_deinit();
- #else
- exit(0);
- #endif
- } else {
- view_append(win_view(TAGS),
- "File is modified. Repeat action twice in < 250ms to quit.");
- }
- before = now;
-}
-
-static bool changed_externally(Buf* buf) {
- bool modified = (buf->modtime != modtime(buf->path));
- if (modified) {
- view_append(win_view(TAGS),
- "File modified externally: Reload, Overwrite, or {SaveAs }");
- }
- return modified;
-}
-
-static void overwrite(void) {
- trim_whitespace();
- buf_save(win_buf(EDIT));
-}
-
-static void save(void) {
- if (!changed_externally(win_buf(EDIT)))
- overwrite();
-}
-
-static void reload(void) {
- view_reload(win_view(EDIT));
-}
-
-/* Mouse Handling
- ******************************************************************************/
-void onmouseleft(WinRegion id, bool pressed, size_t row, size_t col) {
- static int count = 0;
- static uint64_t before = 0;
- if (!pressed) return;
- uint64_t now = getmillis();
- count = ((now-before) <= 250 ? count+1 : 1);
- before = now;
-
- if (count == 1) {
- if (x11_keymodsset(ModShift))
- view_selext(win_view(id), row, col);
- else
- view_setcursor(win_view(id), row, col);
- } else if (count == 2) {
- view_select(win_view(id), row, col);
- } else if (count == 3) {
- view_selword(win_view(id), row, col);
- }
-}
-
-void onmousemiddle(WinRegion id, bool pressed, size_t row, size_t col) {
- if (pressed) return;
- if (win_btnpressed(MouseLeft)) {
- cut();
- } else {
- char* str = view_fetch(win_view(id), row, col);
- if (str) exec(str);
- free(str);
- }
-}
-
-void onmouseright(WinRegion id, bool pressed, size_t row, size_t col) {
- if (pressed) return;
- if (win_btnpressed(MouseLeft)) {
- paste();
- } else {
- SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
- free(SearchTerm);
- SearchTerm = view_fetch(win_view(id), row, col);
- if (view_findstr(win_view(EDIT), SearchDir, SearchTerm)) {
- win_setregion(EDIT);
- win_warpptr(EDIT);
- }
- }
-}
-
-/* Keyboard Handling
- ******************************************************************************/
-static void saveas(char* arg) {
- if (arg) {
- char* path = win_buf(EDIT)->path;
- win_buf(EDIT)->path = stringdup(arg);
- buf_save(win_buf(EDIT));
- free(path);
- }
-}
-
-static void tag_undo(void) {
- view_undo(win_view(EDIT));
-}
-
-static void tag_redo(void) {
- view_redo(win_view(EDIT));
-}
-
-static void search(void) {
- char* str;
- SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
- if (x11_keymodsset(ModAlt) && SearchTerm)
- str = stringdup(SearchTerm);
- else
- str = view_getctx(win_view(FOCUSED));
- 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) {
- char* str = view_getcmd(win_view(FOCUSED));
- if (str) exec(str);
- free(str);
-}
-
-static void find(char* arg) {
- SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1);
- view_findstr(win_view(EDIT), SearchDir, arg);
-}
-
-static void open_file(void) {
- char* file = cmdread(PickFileCmd, NULL);
- if (file) {
- file = chomp(file);
- if (!win_buf(EDIT)->path && !win_buf(EDIT)->modified) {
- buf_load(win_buf(EDIT), file);
- } else {
- OpenCmd[1] = file;
- cmdrun(OpenCmd, NULL);
- }
- }
- free(file);
-}
-
-static void pick_symbol(char* symbol) {
- PickTagCmd[1] = "fetch";
- PickTagCmd[3] = symbol;
- char* pick = cmdread(PickTagCmd, NULL);
- if (pick) {
- Buf* buf = win_buf(EDIT);
- if (buf->path && 0 == strncmp(buf->path, pick, strlen(buf->path))) {
- view_setln(win_view(EDIT), strtoul(strrchr(pick, ':')+1, NULL, 0));
- win_setregion(EDIT);
- } else {
- if (!buf->path && !buf->modified) {
- view_init(win_view(EDIT), pick, onerror);
- } else {
- OpenCmd[1] = chomp(pick);
- cmdrun(OpenCmd, NULL);
- }
- }
- }
-}
-
-static void pick_ctag(void) {
- pick_symbol(NULL);
-}
-
-static void complete(void) {
- View* view = win_view(FOCUSED);
- buf_getword(&(view->buffer), risword, &(view->selection));
- view->selection.end = buf_byrune(&(view->buffer), view->selection.end, RIGHT);
- PickTagCmd[1] = "print";
- PickTagCmd[3] = view_getstr(view, NULL);
- char* pick = cmdread(PickTagCmd, NULL);
- if (pick)
- view_putstr(view, chomp(pick));
- free(PickTagCmd[3]);
-}
-
-static void jump_to(char* arg) {
- if (arg) {
- size_t line = strtoul(arg, NULL, 0);
- if (line) {
- view_setln(win_view(EDIT), line);
- win_setregion(EDIT);
- } else {
- pick_symbol(arg);
- }
- }
-}
-
-static void goto_ctag(void) {
- char* str = view_getctx(win_view(FOCUSED));
- jump_to(str);
- free(str);
-}
-
-static void tabs(void) {
- bool enabled = !(win_buf(EDIT)->expand_tabs);
- win_buf(EDIT)->expand_tabs = enabled;
- win_buf(TAGS)->expand_tabs = enabled;
-}
-
-static void indent(void) {
- bool enabled = !(win_buf(EDIT)->copy_indent);
- win_buf(EDIT)->copy_indent = enabled;
- win_buf(TAGS)->copy_indent = enabled;
-}
-
-static void del_indent(void) {
- view_indent(win_view(FOCUSED), LEFT);
-}
-
-static void add_indent(void) {
- view_indent(win_view(FOCUSED), RIGHT);
-}
-
-static void eol_mode(void) {
- int crlf = win_buf(EDIT)->crlf;
- win_buf(EDIT)->crlf = !crlf;
- win_buf(TAGS)->crlf = !crlf;
- exec(crlf ? "|dos2unix" : "|unix2dos");
-}
-
-static void new_win(void) {
- cmd_exec("!edit");
-}
-
-static void newline(void) {
- View* view = win_view(FOCUSED);
- if (x11_keymodsset(ModShift)) {
- view_byline(view, UP, false);
- view_bol(view, false);
- if (view->selection.end == 0) {
- view_insert(view, true, '\n');
- view->selection = (Sel){0,0,0};
- return;
- }
- }
- view_eol(view, false);
- view_insert(view, true, '\n');
-}
-
-void highlight(void) {
- view_selctx(win_view(FOCUSED));
-}
-
-/* Main Routine
- ******************************************************************************/
-static Tag Builtins[] = {
- { .tag = "Cut", .action.noarg = cut },
- { .tag = "Copy", .action.noarg = copy },
- { .tag = "Eol", .action.noarg = eol_mode },
- { .tag = "Find", .action.arg = find },
- { .tag = "GoTo", .action.arg = jump_to },
- { .tag = "Indent", .action.noarg = indent },
- { .tag = "Overwrite", .action.noarg = overwrite },
- { .tag = "Paste", .action.noarg = paste },
- { .tag = "Quit", .action.noarg = quit },
- { .tag = "Redo", .action.noarg = tag_redo },
- { .tag = "Reload", .action.noarg = reload },
- { .tag = "Save", .action.noarg = save },
- { .tag = "SaveAs", .action.arg = saveas },
- { .tag = "Tabs", .action.noarg = tabs },
- { .tag = "Undo", .action.noarg = tag_undo },
- { .tag = NULL, .action.noarg = NULL }
-};
-
-static KeyBinding Bindings[] = {
- /* Cursor Movements */
- { ModAny, KEY_HOME, cursor_home },
- { ModAny, KEY_END, cursor_end },
- { ModAny, KEY_UP, cursor_up },
- { ModAny, KEY_DOWN, cursor_dn },
- { ModAny, KEY_LEFT, cursor_left },
- { ModAny, KEY_RIGHT, cursor_right },
-
- /* Standard Unix Shortcuts */
- { ModCtrl, 'u', del_to_bol },
- { ModCtrl, 'k', del_to_eol },
- { ModCtrl, 'w', del_to_bow },
- { ModCtrl, 'a', cursor_bol },
- { ModCtrl, 'e', cursor_eol },
-
- /* Standard Text Editing Shortcuts */
- { ModCtrl, 's', save },
- { ModCtrl, 'z', undo },
- { ModCtrl, 'y', redo },
- { ModCtrl, 'x', cut },
- { ModCtrl, 'c', copy },
- { ModCtrl, 'v', paste },
- { ModCtrl, 'j', join_lines },
- { ModCtrl, 'l', select_line },
-
- /* Block Indent */
- { ModCtrl, '[', del_indent },
- { ModCtrl, ']', add_indent },
-
- /* Common Special Keys */
- { ModNone, KEY_PGUP, page_up },
- { ModNone, KEY_PGDN, page_dn },
- { ModAny, KEY_DELETE, delete },
- { ModAny, KEY_BACKSPACE, backspace },
-
- /* Implementation Specific */
- { ModNone, KEY_ESCAPE, select_prev },
- { ModCtrl, 't', change_focus },
- { ModCtrl, 'q', quit },
- { ModCtrl, 'h', highlight },
- { ModCtrl, 'f', search },
- { ModCtrl|ModShift, 'f', search },
- { ModCtrl|ModAlt, 'f', search },
- { ModCtrl|ModAlt|ModShift, 'f', search },
- { ModCtrl, 'd', execute },
- { ModCtrl, 'o', open_file },
- { ModCtrl, 'p', pick_ctag },
- { ModCtrl, 'g', goto_ctag },
- { ModCtrl, 'n', new_win },
- { ModCtrl, '\n', newline },
- { ModCtrl|ModShift, '\n', newline },
- { ModCtrl, ' ', complete },
- { 0, 0, 0 }
-};
-
-void onscroll(double percent) {
- size_t bend = buf_end(win_buf(EDIT));
- size_t off = (size_t)((double)bend * percent);
- view_scrollto(win_view(EDIT), (off >= bend ? bend : off));
-}
-
-void onfocus(bool focused) {
- /* notify the user if the file has changed externally */
- (void)changed_externally(win_buf(EDIT));
-}
-
-void onupdate(void) {
- static char status_bytes[256];
- memset(status_bytes, 0, sizeof(status_bytes));
- char* status = status_bytes;
- Buf* buf = win_buf(EDIT);
- *(status++) = (buf->charset == BINARY ? 'B' : 'U');
- *(status++) = (buf->crlf ? 'C' : 'N');
- *(status++) = (buf->expand_tabs ? 'S' : 'T');
- *(status++) = (buf->copy_indent ? 'I' : 'i');
- *(status++) = (SearchDir < 0 ? '<' : '>');
- *(status++) = (buf->modified ? '*' : ' ');
- *(status++) = ' ';
- char* path = (buf->path ? buf->path : "*scratch*");
- size_t remlen = sizeof(status_bytes) - strlen(status_bytes) - 1;
- strncat(status, path, remlen);
- win_settext(STATUS, status_bytes);
- win_view(STATUS)->selection = (Sel){0,0,0};
-}
-
-void onlayout(void) {
- /* calculate and update scroll region */
- 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);
- win_setscroll(scroll_off, scroll_vis);
-}
-
-void onshutdown(void) {
- quit();
-}
-
-#ifndef TEST
-int main(int argc, char** argv) {
- /* setup the shell */
- ShellCmd[0] = getenv("SHELL");
- if (!ShellCmd[0]) ShellCmd[0] = "/bin/sh";
- /* Create the window and enter the event loop */
- win_window("edit", onerror);
- char* tags = getenv("EDITTAGS");
- win_settext(TAGS, (tags ? tags : DEFAULT_TAGS));
- win_setruler(80);
- view_init(win_view(EDIT), (argc > 1 ? argv[1] : NULL), onerror);
- win_setkeys(Bindings);
- win_loop();
- return 0;
-}
-#endif