-#include <stdc.h>\r
-#include <utf.h>\r
-#include <edit.h>\r
-#include <x11.h>\r
-#include <win.h>\r
-#include <ctype.h>\r
-\r
-typedef struct {\r
- uint64_t time;\r
- uint8_t count;\r
- bool pressed;\r
- int region;\r
-} ButtonState;\r
-\r
-static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols);\r
-static WinRegion getregion(size_t x, size_t y);\r
-static void onredraw(int height, int width);\r
-static void oninput(int mods, Rune key);\r
-static void onmouse(MouseAct act, MouseBtn btn, int x, int y);\r
-static void onshutdown(void);\r
-static void mouse_wheelup(WinRegion id, size_t count, size_t row, size_t col);\r
-static void mouse_wheeldn(WinRegion id, size_t count, size_t row, size_t col);\r
-\r
-static XFont Font;\r
-static XConfig Config = {\r
- .redraw = onredraw,\r
- .handle_key = oninput,\r
- .handle_mouse = onmouse,\r
- .shutdown = onshutdown,\r
- .palette = COLOR_PALETTE\r
-};\r
-\r
-void (*MouseActs[MOUSE_BTN_COUNT])(WinRegion id, size_t count, size_t row, size_t col) = {\r
- [MOUSE_BTN_LEFT] = mouse_left,\r
- [MOUSE_BTN_MIDDLE] = mouse_middle,\r
- [MOUSE_BTN_RIGHT] = mouse_right,\r
- [MOUSE_BTN_WHEELUP] = mouse_wheelup,\r
- [MOUSE_BTN_WHEELDOWN] = mouse_wheeldn,\r
-};\r
-\r
-static WinRegion Focused = EDIT;\r
-static Region Regions[NREGIONS] = {0};\r
-static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0};\r
-KeyBinding* Keys = NULL;\r
-\r
-void win_init(char* name) {\r
- for (int i = 0; i < SCROLL; i++)\r
- view_init(&(Regions[i].view), NULL);\r
- x11_init(&Config);\r
- Font = x11_font_load(FONTNAME);\r
- x11_window(name, Width, Height);\r
-}\r
-\r
-void win_loop(void) {\r
- x11_show();\r
- x11_loop();\r
-}\r
-\r
-void win_settext(WinRegion id, char* text) {\r
- View* view = win_view(id);\r
- view->buffer.gapstart = view->buffer.bufstart;\r
- view->buffer.gapend = view->buffer.bufend;\r
- view->selection = (Sel){0,0,0};\r
- view_putstr(view, text);\r
- view_selprev(view); // clear the selection\r
- buf_logclear(&(view->buffer));\r
-}\r
-\r
-void win_setkeys(KeyBinding* bindings) {\r
- Keys = bindings;\r
-}\r
-\r
-bool win_btnpressed(MouseBtn btn) {\r
- return MouseBtns[btn].pressed;\r
-}\r
-\r
-WinRegion win_getregion(void) {\r
- return Focused;\r
-}\r
-\r
-void win_setregion(WinRegion id) {\r
- Focused = id;\r
-}\r
-\r
-View* win_view(WinRegion id) {\r
- if (id == FOCUSED) id = Focused;\r
- return &(Regions[id].view);\r
-}\r
-\r
-Buf* win_buf(WinRegion id) {\r
- if (id == FOCUSED) id = Focused;\r
- return &(Regions[id].view.buffer);\r
-}\r
-\r
-Sel* win_sel(WinRegion id) {\r
- if (id == FOCUSED) id = Focused;\r
- return &(Regions[id].view.selection);\r
-}\r
-\r
-static void layout(int width, int height) {\r
- size_t fheight = x11_font_height(Font);\r
- size_t fwidth = x11_font_width(Font);\r
- View* statview = win_view(STATUS);\r
- View* tagview = win_view(TAGS);\r
- View* editview = win_view(EDIT);\r
- \r
- /* update the text views and region positions and sizes */\r
- for (int i = 0; i < SCROLL; i++) {\r
- Regions[i].x = 2;\r
- Regions[i].y = 2;\r
- Regions[i].csrx = SIZE_MAX;\r
- Regions[i].csry = SIZE_MAX;\r
- Regions[i].width = (width - 4);\r
- Regions[i].height = fheight;\r
- }\r
- \r
- /* place the status region */\r
- view_resize(statview, 1, Regions[STATUS].width / fwidth);\r
- view_update(statview, &(Regions[STATUS].csrx), &(Regions[STATUS].csry));\r
- \r
- /* Place the tag region relative to status */\r
- Regions[TAGS].y = 5 + Regions[STATUS].y + Regions[STATUS].height;\r
- size_t maxtagrows = ((height - Regions[TAGS].y - 5) / 4) / fheight;\r
- size_t tagcols = Regions[TAGS].width / fwidth;\r
- size_t tagrows = view_limitrows(tagview, maxtagrows, tagcols);\r
- Regions[TAGS].height = tagrows * fheight;\r
- view_resize(tagview, tagrows, tagcols);\r
- view_update(tagview, &(Regions[TAGS].csrx), &(Regions[TAGS].csry));\r
- \r
- /* Place the edit region relative to status */\r
- Regions[EDIT].y = 5 + Regions[TAGS].y + Regions[TAGS].height;\r
- Regions[EDIT].height = (height - Regions[EDIT].y - 5);\r
- view_resize(editview, Regions[EDIT].height / fheight, Regions[EDIT].width / fwidth);\r
- view_update(editview, &(Regions[EDIT].csrx), &(Regions[EDIT].csry));\r
-}\r
-\r
-static void onredraw(int width, int height) {\r
- size_t fheight = x11_font_height(Font);\r
- size_t fwidth = x11_font_width(Font);\r
- onupdate(); // Let the user program update the status and such\r
- /* layout and draw the three text regions */\r
- layout(width, height);\r
- for (int i = 0; i < SCROLL; i++) {\r
- View* view = win_view(i);\r
- x11_draw_rect((i == TAGS ? CLR_BASE02 : CLR_BASE03), \r
- 0, Regions[i].y - 3, width, Regions[i].height + 8);\r
- x11_draw_rect(CLR_BASE01, 0, Regions[i].y - 3, Regions[i].width + 4, 1);\r
- for (size_t y = 0; y < view->nrows; y++) {\r
- Row* row = view_getrow(view, y);\r
- draw_glyphs(Regions[i].x, Regions[i].y + ((y+1) * fheight), row->cols, row->rlen, row->len);\r
- }\r
- }\r
- \r
- /* draw the scroll region */\r
- \r
- /* place the cursor on screen */\r
- x11_draw_rect(CLR_BASE3, \r
- Regions[Focused].x + (Regions[Focused].csrx * fwidth), \r
- Regions[Focused].y + (Regions[Focused].csry * fheight), \r
- 1, fheight);\r
- \r
- /* adjust the mouse location */\r
- if (Regions[Focused].warp_ptr) {\r
- Regions[Focused].warp_ptr = false;\r
- size_t x = Regions[Focused].x + (Regions[Focused].csrx * fwidth) - (fwidth/2);\r
- size_t y = Regions[Focused].y + (Regions[Focused].csry * fheight) + (fheight/2);\r
- x11_warp_mouse(x,y);\r
- }\r
-}\r
-\r
-static void oninput(int mods, Rune key) {\r
- /* handle the proper line endings */\r
- if (key == '\r') key = '\n';\r
- if (key == '\n' && win_view(FOCUSED)->buffer.crlf) key = RUNE_CRLF;\r
- /* search for a key binding entry */\r
- uint32_t mkey = tolower(key);\r
- for (KeyBinding* bind = Keys; bind && bind->key; bind++) {\r
- if ((mkey == bind->key) && (bind->mods == ModAny || bind->mods == mods)) {\r
- bind->action();\r
- return;\r
- }\r
- }\r
- \r
- /* fallback to just inserting the rune if it doesn't fall in the private use area.\r
- * the private use area is used to encode special keys */\r
- if (key < 0xE000 || key > 0xF8FF)\r
- view_insert(win_view(FOCUSED), true, key);\r
-}\r
-\r
-static void onmouse(MouseAct act, MouseBtn btn, int x, int y) {\r
- WinRegion id = getregion(x, y);\r
- size_t row = (y-Regions[id].y) / x11_font_height(Font);\r
- size_t col = (x-Regions[id].x) / x11_font_width(Font);\r
- if (act == MOUSE_ACT_MOVE) {\r
- if (MouseBtns[MOUSE_BTN_LEFT].pressed) {\r
- WinRegion selid = MouseBtns[MOUSE_BTN_LEFT].region;\r
- if (MouseBtns[MOUSE_BTN_LEFT].count == 1) {\r
- view_setcursor(win_view(selid), row, col);\r
- MouseBtns[MOUSE_BTN_LEFT].count = 0;\r
- } else {\r
- view_selext(win_view(selid), row, col);\r
- }\r
- } else if (id == TAGS || id == EDIT) {\r
- Focused = id;\r
- }\r
- } else {\r
- MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN);\r
- if (MouseBtns[btn].pressed) {\r
- /* update the number of clicks and click time */\r
- uint32_t now = getmillis();\r
- uint32_t elapsed = now - MouseBtns[btn].time;\r
- MouseBtns[btn].time = now;\r
- MouseBtns[btn].region = id;\r
- if (elapsed <= 250)\r
- MouseBtns[btn].count++;\r
- else\r
- MouseBtns[btn].count = 1;\r
- } else if (MouseBtns[btn].count > 0) {\r
- /* execute the action on button release */\r
- if (MouseActs[btn])\r
- MouseActs[btn](MouseBtns[btn].region, MouseBtns[btn].count, row, col);\r
- }\r
- }\r
-}\r
-\r
-static void onshutdown(void) {\r
- x11_deinit();\r
-}\r
-\r
-static void mouse_wheelup(WinRegion id, size_t count, size_t row, size_t col) {\r
- view_scroll(win_view(id), -ScrollLines);\r
-}\r
-\r
-static void mouse_wheeldn(WinRegion id, size_t count, size_t row, size_t col) {\r
- view_scroll(win_view(id), +ScrollLines);\r
-}\r
-\r
-/*****************************************************************************/\r
-\r
-static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols) {\r
- XGlyphSpec specs[rlen];\r
- size_t i = 0;\r
- while (rlen && i < ncols) {\r
- int numspecs = 0;\r
- uint32_t attr = glyphs[i].attr;\r
- while (i < ncols && glyphs[i].attr == attr) {\r
- x11_font_getglyph(Font, &(specs[numspecs]), glyphs[i].rune);\r
- specs[numspecs].x = x;\r
- specs[numspecs].y = y - x11_font_descent(Font);\r
- x += x11_font_width(Font);\r
- numspecs++;\r
- i++;\r
- /* skip over null chars which mark multi column runes */\r
- for (; i < ncols && !glyphs[i].rune; i++)\r
- x += x11_font_width(Font);\r
- }\r
- /* Draw the glyphs with the proper colors */\r
- uint8_t bg = attr >> 8;\r
- uint8_t fg = attr & 0xFF;\r
- x11_draw_glyphs(fg, bg, specs, numspecs);\r
- rlen -= numspecs;\r
- }\r
-}\r
-\r
-static WinRegion getregion(size_t x, size_t y) {\r
- for (int i = 0; i < NREGIONS; i++) {\r
- size_t startx = Regions[i].x, endx = startx + Regions[i].width;\r
- size_t starty = Regions[i].y, endy = starty + Regions[i].height;\r
- if ((startx <= x && x <= endx) && (starty <= y && y <= endy))\r
- return (WinRegion)i;\r
- }\r
- return NREGIONS;\r
-}\r
-\r
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
+#include <x11.h>
+#include <win.h>
+#include <ctype.h>
+
+typedef struct {
+ uint64_t time;
+ uint8_t count;
+ bool pressed;
+ int region;
+} ButtonState;
+
+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 void onredraw(int height, int width);
+static void oninput(int mods, Rune key);
+static void onmouse(MouseAct act, MouseBtn btn, int x, int y);
+static void onshutdown(void);
+static void mouse_wheelup(WinRegion id, size_t count, size_t row, size_t col);
+static void mouse_wheeldn(WinRegion id, size_t count, size_t row, size_t col);
+
+static XFont Font;
+static XConfig Config = {
+ .redraw = onredraw,
+ .handle_key = oninput,
+ .handle_mouse = onmouse,
+ .shutdown = onshutdown,
+ .palette = COLOR_PALETTE
+};
+
+void (*MouseActs[MOUSE_BTN_COUNT])(WinRegion id, size_t count, size_t row, size_t col) = {
+ [MOUSE_BTN_LEFT] = mouse_left,
+ [MOUSE_BTN_MIDDLE] = mouse_middle,
+ [MOUSE_BTN_RIGHT] = mouse_right,
+ [MOUSE_BTN_WHEELUP] = mouse_wheelup,
+ [MOUSE_BTN_WHEELDOWN] = mouse_wheeldn,
+};
+
+static WinRegion Focused = EDIT;
+static Region Regions[NREGIONS] = {0};
+static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0};
+KeyBinding* Keys = NULL;
+
+void win_init(char* name) {
+ for (int i = 0; i < SCROLL; i++)
+ view_init(&(Regions[i].view), NULL);
+ x11_init(&Config);
+ Font = x11_font_load(FONTNAME);
+ x11_window(name, Width, Height);
+}
+
+void win_loop(void) {
+ x11_show();
+ x11_loop();
+}
+
+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_setkeys(KeyBinding* bindings) {
+ Keys = bindings;
+}
+
+bool win_btnpressed(MouseBtn btn) {
+ return MouseBtns[btn].pressed;
+}
+
+WinRegion win_getregion(void) {
+ return Focused;
+}
+
+void win_setregion(WinRegion id) {
+ Focused = id;
+}
+
+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);
+}
+
+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);
+ view_update(statview, &(Regions[STATUS].csrx), &(Regions[STATUS].csry));
+
+ /* 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);
+ view_update(tagview, &(Regions[TAGS].csrx), &(Regions[TAGS].csry));
+
+ /* 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);
+ view_update(editview, &(Regions[EDIT].csrx), &(Regions[EDIT].csry));
+}
+
+static void onredraw(int width, int height) {
+ size_t fheight = x11_font_height(Font);
+ size_t fwidth = x11_font_width(Font);
+ onupdate(); // Let the user program update the status and such
+ /* layout and draw the three text regions */
+ layout(width, height);
+ 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);
+ 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 */
+ x11_draw_rect(CLR_BASE01, Regions[SCROLL].width, Regions[SCROLL].y-2, 1, Regions[SCROLL].height);
+
+ /* 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_warp_mouse(x,y);
+ }
+}
+
+static void oninput(int mods, Rune key) {
+ /* handle the proper line endings */
+ if (key == '\r') key = '\n';
+ if (key == '\n' && win_view(FOCUSED)->buffer.crlf) key = RUNE_CRLF;
+ /* 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)
+ view_insert(win_view(FOCUSED), true, key);
+}
+
+static void onmouse(MouseAct act, MouseBtn btn, 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 (act == MOUSE_ACT_MOVE) {
+ if (MouseBtns[MOUSE_BTN_LEFT].pressed) {
+ WinRegion selid = MouseBtns[MOUSE_BTN_LEFT].region;
+ if (MouseBtns[MOUSE_BTN_LEFT].count == 1) {
+ view_setcursor(win_view(selid), row, col);
+ MouseBtns[MOUSE_BTN_LEFT].count = 0;
+ } else {
+ view_selext(win_view(selid), row, col);
+ }
+ } else if (id == TAGS || id == EDIT) {
+ Focused = id;
+ }
+ } else {
+ MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN);
+ if (MouseBtns[btn].pressed) {
+ /* update the number of clicks and click time */
+ uint32_t now = getmillis();
+ uint32_t elapsed = now - MouseBtns[btn].time;
+ MouseBtns[btn].time = now;
+ MouseBtns[btn].region = id;
+ if (elapsed <= 250)
+ MouseBtns[btn].count++;
+ else
+ MouseBtns[btn].count = 1;
+ } else if (MouseBtns[btn].count > 0) {
+ /* execute the action on button release */
+ if (MouseActs[btn])
+ MouseActs[btn](MouseBtns[btn].region, MouseBtns[btn].count, row, col);
+ }
+ }
+}
+
+static void onshutdown(void) {
+ x11_deinit();
+}
+
+static void mouse_wheelup(WinRegion id, size_t count, size_t row, size_t col) {
+ if (id == SCROLL) id = EDIT;
+ view_scroll(win_view(id), -ScrollLines);
+}
+
+static void mouse_wheeldn(WinRegion id, size_t count, size_t row, size_t col) {
+ if (id == SCROLL) id = EDIT;
+ 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;
+}
+