]> git.mdlowis.com Git - projs/tide.git/commitdiff
major reorginization to allow reuse of gui and edit buffer code
authorMichael D. Lowis <mike@mdlowis.com>
Fri, 4 Nov 2016 00:01:15 +0000 (20:01 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Fri, 4 Nov 2016 00:01:15 +0000 (20:01 -0400)
19 files changed:
Makefile
inc/X.h [new file with mode: 0644]
inc/atf.h [moved from tests/atf.h with 100% similarity]
inc/edit.h [moved from edit.h with 57% similarity]
inc/stdc.h [new file with mode: 0644]
inc/utf.h [new file with mode: 0644]
libedit/buf.c [moved from buf.c with 99% similarity]
libedit/charset.c [moved from charset.c with 97% similarity]
libedit/keyboard.c [moved from keyboard.c with 97% similarity]
libedit/mouse.c [moved from mouse.c with 98% similarity]
libedit/screen.c [moved from screen.c with 98% similarity]
libedit/utf8.c [moved from utf8.c with 95% similarity]
libedit/utils.c [moved from utils.c with 92% similarity]
libx/x11.c [new file with mode: 0644]
tests/buf.c
tests/utf8.c
unittests.c [moved from tests/tests.c with 83% similarity]
xedit.c
xpick.c [new file with mode: 0644]

index dcb9697d5eb42b171633aa26777e311d01593afd..7a305fdfcf997b19c1584912c1723ba3cd88b10d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,32 +1,39 @@
-PREFIX   = /usr/local
-GCOV     = --coverage
-LDFLAGS  = $(GCOV) -L/opt/X11/lib -lX11 -lXft -lfontconfig
-CFLAGS   = $(GCOV) -O0 --std=gnu99 -Wall -Wextra -I. -I/opt/X11/include -I/opt/local/include/freetype2 -I/usr/include/freetype2
-OBJS     = buf.o screen.o utf8.o keyboard.o mouse.o charset.o utils.o
-TESTOBJS = tests/tests.o tests/buf.o tests/utf8.o
+CC = c99
+LDFLAGS = -L/usr/X11/lib -lX11 -lXft -lfontconfig
+CFLAGS = -Os -Iinc/ -I/usr/X11/include -I/usr/X11/include/freetype2
 
-all: edit test
+LIBEDIT_OBJS =         \
+       libedit/buf.o      \
+       libedit/charset.o  \
+       libedit/keyboard.o \
+       libedit/mouse.o    \
+       libedit/screen.o   \
+       libedit/utf8.o     \
+       libedit/utils.o
 
-test: unittests
-       ./unittests
+LIBX_OBJS = \
+       libx/x11.o
 
-edit: xedit.o $(OBJS)
-       $(CC) -o $@ $^ $(LDFLAGS)
+TEST_OBJS =     \
+       unittests.o \
+       tests/buf.o \
+       tests/utf8.o
 
-unittests: $(TESTOBJS) $(OBJS)
-       $(CC) -o $@ $^ $(LDFLAGS)
+all: xedit xpick test
 
-coverage: test
-       gcov -fabc $(OBJS) > coverage.txt
+clean:
+       $(RM) *.o lib*/*.o test/*.o *.a xpick xedit unittests
 
-install:
-       cp
+test: unittests
+       ./unittests
 
-clean:
-       $(RM) edit unittests xedit.o $(OBJS) $(TESTOBJS) coverage.txt
-       $(RM) *.gcno *.gcda *.gcov tests/*.gcno tests/*.gcda tests/*.gcov
+xedit: xedit.o libx.a libedit.a
+xpick: xpick.o libx.a libedit.a
+
+libedit.a: $(LIBEDIT_OBJS)
+       $(AR) rcs $@ $^
 
-$(OBJS): edit.h Makefile
-xedit.o: edit.h Makefile
+libx.a: $(LIBX_OBJS)
+       $(AR) rcs $@ $^
 
-.PHONY: all test
+unittests: $(TEST_OBJS) libedit.a
diff --git a/inc/X.h b/inc/X.h
new file mode 100644 (file)
index 0000000..0301c94
--- /dev/null
+++ b/inc/X.h
@@ -0,0 +1,136 @@
+#include <X11/Xft/Xft.h>
+#include <stdint.h>
+
+typedef enum {
+    MOUSE_ACT_UP,
+    MOUSE_ACT_DOWN,
+    MOUSE_ACT_MOVE
+} MouseAct;
+
+typedef enum {
+        MOUSE_BTN_LEFT = 0,
+        MOUSE_BTN_MIDDLE,
+        MOUSE_BTN_RIGHT,
+        MOUSE_BTN_WHEELUP,
+        MOUSE_BTN_WHEELDOWN,
+        MOUSE_BTN_NONE
+} MouseBtn;
+
+typedef struct {
+    void (*redraw)(int width, int height);
+    void (*handle_key)(uint32_t rune);
+    void (*handle_mouse)(MouseAct act, MouseBtn btn, int x, int y);
+    uint32_t palette[16];
+} XConfig;
+
+#ifndef MAXFONTS
+#define MAXFONTS 16
+#endif
+
+typedef struct {
+    struct {
+        int height;
+        int width;
+        int ascent;
+        int descent;
+        XftFont* match;
+        FcFontSet* set;
+        FcPattern* pattern;
+    } base;
+    struct {
+        XftFont* font;
+        uint32_t unicodep;
+    } cache[MAXFONTS];
+    int ncached;
+} XFont;
+
+typedef struct {
+    uint32_t attr; /* attributes  applied to this cell */
+    uint32_t rune; /* rune value for the cell */
+} XGlyph;
+
+/* 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,
+};
+
+void x11_init(XConfig* cfg);
+void x11_deinit(void);
+void x11_window(char* name, int width, int height);
+void x11_dialog(char* name, int height, int width);
+void x11_show(void);
+void x11_loop(void);
+void x11_draw_rect(int color, int x, int y, int width, int height);
+void x11_draw_glyphs(int fg, int bg, XftGlyphFontSpec* glyphs, size_t nglyphs);
+void x11_getsize(int* width, int* height);
+void x11_warp_mouse(int x, int y);
+void x11_font_load(XFont* font, char* name);
+void x11_font_getglyph(XFont* font, XftGlyphFontSpec* spec, uint32_t rune);
+size_t x11_font_getglyphs(XftGlyphFontSpec* specs, const XGlyph* glyphs, int len, XFont* font, int x, int y);
similarity index 100%
rename from tests/atf.h
rename to inc/atf.h
similarity index 57%
rename from edit.h
rename to inc/edit.h
index 576f8139b61fc7a22cb02996c2cf56d07704a0d0..ef1645d9b2920a83479870fc41f2eebc33ac400e 100644 (file)
--- a/edit.h
@@ -1,30 +1,3 @@
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <string.h>
-
-/* Unicode Handling
- *****************************************************************************/
-enum {
-    UTF_MAX   = 6u,          /* maximum number of bytes that make up a rune */
-    RUNE_SELF = 0x80,        /* byte values larger than this are *not* ascii */
-    RUNE_ERR  = 0xFFFD,      /* rune value representing an error */
-    RUNE_MAX  = 0x10FFFF,    /* Maximum decodable rune value */
-    RUNE_EOF  = UINT32_MAX,  /* rune value representing end of file */
-    RUNE_CRLF = UINT32_MAX-1 /* rune value representing a \r\n sequence */
-};
-
-/* Represents a unicode code point */
-typedef uint32_t Rune;
-
-size_t utf8encode(char str[UTF_MAX], Rune rune);
-bool utf8decode(Rune* rune, size_t* length, int byte);
-Rune fgetrune(FILE* f);
-void fputrune(Rune rune, FILE* f);
-int runewidth(unsigned col, Rune r);
-
 /* Utility Functions
  *****************************************************************************/
 typedef struct {
@@ -34,7 +7,6 @@ typedef struct {
 
 FMap fmap(char* path);
 void funmap(FMap file);
-void die(const char* fmt, ...);
 uint32_t getmillis(void);
 bool risword(Rune r);
 bool risblank(Rune r);
@@ -83,7 +55,6 @@ unsigned buf_redo(Buf* buf, unsigned pos);
 Rune buf_get(Buf* buf, unsigned pos);
 void buf_setlocked(Buf* buf, bool locked);
 bool buf_locked(Buf* buf);
-
 bool buf_iseol(Buf* buf, unsigned pos);
 unsigned buf_bol(Buf* buf, unsigned pos);
 unsigned buf_eol(Buf* buf, unsigned pos);
@@ -117,78 +88,6 @@ void binsave(Buf* buf, FILE* file);
 
 /* Input Handling
  *****************************************************************************/
-/* 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,
-};
-
 /* Define the mouse buttons used for input */
 typedef struct {
     enum {
@@ -257,19 +156,8 @@ enum ColorId {
     CLR_COUNT   = 16
 };
 
-/* Represents an ARGB color value */
-typedef uint32_t Color;
-
-/* two colorscheme variants are supported, a light version and a dark version */
-enum ColorScheme {
-    DARK = 0,
-    LIGHT = 1
-};
-
 /* Global State
  *****************************************************************************/
-/* variable for holding the currently selected color scheme */
-extern enum ColorScheme ColorBase;
 extern Buf Buffer;
 extern unsigned TargetCol;
 extern unsigned SelBeg;
@@ -283,27 +171,6 @@ enum {
     TabWidth    = 4,    /* maximum number of spaces used to represent a tab */
     ScrollLines = 1,    /* number of lines to scroll by for mouse wheel scrolling */
     BufSize     = 8192, /* default buffer size */
-    MaxFonts    = 16    /* maximum number of fonts to cache */
-};
-
-static const Color ColorScheme[CLR_COUNT][2] = {
-/*   Color Name   =   Dark      Light    */
-    [CLR_BASE03]  = { 0x002b36, 0xfdf6e3 },
-    [CLR_BASE02]  = { 0x073642, 0xeee8d5 },
-    [CLR_BASE01]  = { 0x586e75, 0x93a1a1 },
-    [CLR_BASE00]  = { 0x657b83, 0x839496 },
-    [CLR_BASE0]   = { 0x839496, 0x657b83 },
-    [CLR_BASE1]   = { 0x93a1a1, 0x586e75 },
-    [CLR_BASE2]   = { 0xeee8d5, 0x073642 },
-    [CLR_BASE3]   = { 0xfdf6e3, 0x002b36 },
-    [CLR_YELLOW]  = { 0xb58900, 0xb58900 },
-    [CLR_ORANGE]  = { 0xcb4b16, 0xcb4b16 },
-    [CLR_RED]     = { 0xdc322f, 0xdc322f },
-    [CLR_MAGENTA] = { 0xd33682, 0xd33682 },
-    [CLR_VIOLET]  = { 0x6c71c4, 0x6c71c4 },
-    [CLR_BLUE]    = { 0x268bd2, 0x268bd2 },
-    [CLR_CYAN]    = { 0x2aa198, 0x2aa198 },
-    [CLR_GREEN]   = { 0x859900, 0x859900 },
 };
 
 /* choose the font to  use for xft */
diff --git a/inc/stdc.h b/inc/stdc.h
new file mode 100644 (file)
index 0000000..3a12993
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+  @brief Collection of useful C types and functions.
+  @author Michael D. Lowis
+  @license BSD 2-clause License
+*/
+
+/* Standard Macros and Types */
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+/* Useful Standard Functions */
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Type Definitions
+ *****************************************************************************/
+typedef unsigned short ushort;
+typedef unsigned char uchar;
+typedef unsigned long ulong;
+typedef unsigned int uint;
+typedef signed char schar;
+typedef long long vlong;
+typedef unsigned long long uvlong;
+
+typedef uint8_t  uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+
+typedef int8_t  int8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef int64_t int64;
+
+typedef uintptr_t uintptr;
+typedef intptr_t  intptr;
+
+/* Generic Death Function
+ *****************************************************************************/
+void die(const char* msgfmt, ...);
+
+//static char* estrdup(const char *s) {
+//    char* ns = (char*)emalloc(strlen(s) + 1);
+//    strcpy(ns,s);
+//    return ns;
+//}
+
+/* Option Parsing
+ *
+ * This following macros implement a simple POSIX-style option parsing strategy.
+ * They are heavily influenced and inspired by the arg.h file from suckless.org
+ * (http://git.suckless.org/libsl/tree/arg.h). That file is in turn inspired by
+ * the corresponding macros defined in plan9.
+ *
+ * The interface assumes that the main function will have the following
+ * prototype:
+ *
+ * int main(int argc, char** argv);
+ *
+ * An example usage of the interface would look something like the follwoing:
+ *
+ * char* ARGV0;
+ * int main(int argc, char** argv) {
+ *     OPTBEGIN {
+ *         case 'a': printf("Simple option\n"); break;
+ *         case 'b': printf("Option with arg: %s\n", OPTARG()); break;
+ *         default:  printf("Unknown option!\n");
+ *     } OPTEND;
+ *     return 0;
+ * }
+ */
+
+/* This variable contains the value of argv[0] so that it can be referenced
+ * again once the option parsing is done. This variable must be defined by the
+ * program.
+ *
+ * NOTE: Ensure that you define this variable with external linkage (i.e. not
+ * static) */
+extern char* ARGV0;
+
+/* This is a helper function used by the following macros to parse the next
+ * option from the command line. */
+static inline char* _getopt_(int* p_argc, char*** p_argv) {
+    if (!(*p_argv)[0][1] && !(*p_argv)[1]) {
+        return (char*)0;
+    } else if ((*p_argv)[0][1]) {
+        return &(*p_argv)[0][1];
+    } else {
+        *p_argv = *p_argv + 1;
+        *p_argc = *p_argc - 1;
+        return (*p_argv)[0];
+    }
+}
+
+/* This macro is almost identical to the ARGBEGIN macro from suckless.org. If
+ * it ain't broke, don't fix it. */
+#define OPTBEGIN                                                              \
+    for (                                                                     \
+        ARGV0 = *argv, argc--, argv++;                                        \
+        argv[0] && argv[0][1] && argv[0][0] == '-';                           \
+        argc--, argv++                                                        \
+    ) {                                                                       \
+        int brk_; char argc_ , **argv_, *optarg_;                             \
+        if (argv[0][1] == '-' && !argv[0][2]) {                               \
+            argv++, argc--; break;                                            \
+        }                                                                     \
+        for (brk_=0, argv[0]++, argv_=argv; argv[0][0] && !brk_; argv[0]++) { \
+            if (argv_ != argv) break;                                         \
+            argc_ = argv[0][0];                                               \
+            switch (argc_)
+
+/* Terminate the option parsing. */
+#define OPTEND }}
+
+/* Get the current option character */
+#define OPTC() (argc_)
+
+/* Get an argument from the command line and return it as a string. If no
+ * argument is available, this macro returns NULL */
+#define OPTARG() \
+    (optarg_ = _getopt_(&argc,&argv), brk_ = (optarg_!=0), optarg_)
+
+/* Get an argument from the command line and return it as a string. If no
+ * argument is available, this macro executes the provided code. If that code
+ * returns, then abort is called. */
+#define EOPTARG(code) \
+    (optarg_ = _getopt_(&argc,&argv), \
+    (!optarg_ ? ((code), abort(), (char*)0) : (brk_ = 1, optarg_)))
+
+/* Helper macro to recognize number options */
+#define OPTNUM \
+    case '0':  \
+    case '1':  \
+    case '2':  \
+    case '3':  \
+    case '4':  \
+    case '5':  \
+    case '6':  \
+    case '7':  \
+    case '8':  \
+    case '9'
+
+/* Helper macro to recognize "long" options ala GNU style. */
+#define OPTLONG \
+    case '-'
+
+/* Error Handling
+ *****************************************************************************/
+#ifdef NDEBUG
+    #define debug(msg, ...) \
+        ((void)0)
+#else
+    #define debug(msg, ...) \
+        fprintf(stderr, "DEBUG %s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__)
+#endif
+
+#define errnostr() \
+    (errno == 0 ? "None" : strerror(errno))
+
+#define print_error(msg, ...) \
+    fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " msg "\n", __FILE__, __LINE__, errnostr(), ##__VA_ARGS__)
+
+#define check(expr, msg, ...) \
+    if(!(expr)) { print_error(msg, ##__VA_ARGS__); errno=0; goto error; }
+
+#define sentinel(msg, ...) \
+    { print_error(msg, ##__VA_ARGS__); errno=0; goto error; }
+
+/* Miscellaneous
+ *****************************************************************************/
+#ifndef nelem
+    #define nelem(x) \
+        (sizeof(x)/sizeof((x)[0]))
+#endif
+
+#ifndef container_of
+    #define container_of(obj, type, member) \
+        (type*)((uintptr_t)obj - offsetof(type, member))
+#endif
+
+#define concat(a,b) \
+    a##b
+
+#define ident(a) \
+    concat(id, a)
+
+#define unique_id \
+    ident(__LINE__)
+
+#ifndef static_assert
+    #define static_assert(expr) \
+        typedef char unique_id[( expr )?1:-1]
+#endif
diff --git a/inc/utf.h b/inc/utf.h
new file mode 100644 (file)
index 0000000..465b8b4
--- /dev/null
+++ b/inc/utf.h
@@ -0,0 +1,15 @@
+#define UTF_MAX   6u           /* maximum number of bytes that make up a rune */
+#define RUNE_SELF 0x80         /* byte values larger than this are *not* ascii */
+#define RUNE_ERR  0xFFFD       /* rune value representing an error */
+#define RUNE_MAX  0x10FFFF     /* Maximum decodable rune value */
+#define RUNE_EOF  UINT32_MAX   /* rune value representing end of file */
+#define RUNE_CRLF UINT32_MAX-1 /* rune value representing a \r\n sequence */
+
+/* Represents a unicode code point */
+typedef uint32_t Rune;
+
+size_t utf8encode(char str[UTF_MAX], Rune rune);
+bool utf8decode(Rune* rune, size_t* length, int byte);
+Rune fgetrune(FILE* f);
+void fputrune(Rune rune, FILE* f);
+int runewidth(unsigned col, Rune r);
similarity index 99%
rename from buf.c
rename to libedit/buf.c
index 9cd099c46b4e7d92f17ebdc6c3a67dec16d3b977..63f257de841e7c84081a7ef39f6c513ef4b8e4f6 100644 (file)
--- a/buf.c
@@ -1,9 +1,6 @@
-#define _GNU_SOURCE
-#include <string.h>
-#include <assert.h>
-#include <wchar.h>
-
-#include "edit.h"
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 
 void buf_load(Buf* buf, char* path) {
     buf_setlocked(buf, false);
similarity index 97%
rename from charset.c
rename to libedit/charset.c
index 5ef1e07491f115e96804a6ba1612225253b86d5f..6051db1f622501e6337f00dff3914b9691e6cf7f 100644 (file)
--- a/charset.c
@@ -1,4 +1,6 @@
-#include "edit.h"
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 
 static const struct {
     int type;
similarity index 97%
rename from keyboard.c
rename to libedit/keyboard.c
index 59df3a2bf955e7e2a290f28311b7c93f62923533..c05df34def76be63e9d18cf291412f5b7e969e4b 100644 (file)
@@ -1,8 +1,7 @@
-#include "edit.h"
-
-static void toggle_colors(void) {
-    ColorBase = !ColorBase;
-}
+#include <stdc.h>
+#include <X.h>
+#include <utf.h>
+#include <edit.h>
 
 static void cursor_up(void) {
     SelBeg = SelEnd = buf_byline(&Buffer, SelEnd, -1);
@@ -134,7 +133,6 @@ typedef struct {
 } KeyBinding_T;
 
 static KeyBinding_T Normal[] = {
-    { KEY_F6,     toggle_colors },
     { KEY_CTRL_Q, quit          },
     { KEY_CTRL_W, write         },
     //{ 'q',        quit          },
@@ -197,7 +195,6 @@ static KeyBinding_T Normal[] = {
 };
 
 static KeyBinding_T Insert[] = {
-    { KEY_F6,        toggle_colors },
     { KEY_UP,        cursor_up     },
     { KEY_DOWN,      cursor_dn     },
     { KEY_LEFT,      cursor_left   },
similarity index 98%
rename from mouse.c
rename to libedit/mouse.c
index f6239178a5031513f8e12d1066cfc61e9461ba55..81a5086fd6b33dbf533d147fdddfab09586cd1b8 100644 (file)
--- a/mouse.c
@@ -1,5 +1,6 @@
-#include "edit.h"
-#include <ctype.h>
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 
 void unused(MouseEvent* mevnt) {
     (void)mevnt;
similarity index 98%
rename from screen.c
rename to libedit/screen.c
index 5cd464b7f55c9cc664ef166d83ffed9ef815d59b..cd1c3aeda2f07e89a9f745b9223d12b8cecb0619 100644 (file)
--- a/screen.c
@@ -1,4 +1,6 @@
-#include "edit.h"
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 
 static unsigned NumRows = 0;
 static unsigned NumCols = 0;
@@ -26,6 +28,7 @@ static void screen_reflow(Buf* buf) {
 }
 
 void screen_setsize(Buf* buf, unsigned nrows, unsigned ncols) {
+    if (NumRows == nrows && NumCols == ncols)  return;
     /* free the old row data */
     if (Rows) {
         for (unsigned i = 0; i < NumRows; i++)
similarity index 95%
rename from utf8.c
rename to libedit/utf8.c
index 824da092e04eb309745fac51848073cd254f92db..c96a7a6381604bc5f32bae6f9f0dc6f42fc2f1a6 100644 (file)
--- a/utf8.c
@@ -1,10 +1,6 @@
-/**
-  @brief Simple UTF-8 encoding and decoding routines.
-  @author Michael D. Lowis
-  @license BSD 2-clause License
-*/
-#include "edit.h"
-#define __USE_XOPEN
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 #include <wchar.h>
 
 const uint8_t UTF8_SeqBits[] = { 0x00u, 0x80u, 0xC0u, 0xE0u, 0xF0u, 0xF8u, 0xFCu, 0xFEu };
similarity index 92%
rename from utils.c
rename to libedit/utils.c
index 5fd2278a92fb1559d494993d863ecb61c6d7f61d..7003c338b4140a5da0eae3fec74149817aa379d3 100644 (file)
--- a/utils.c
@@ -1,17 +1,17 @@
-#include "edit.h"
-
-#define _GNU_SOURCE
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 #include <time.h>
 #include <sys/time.h>
-#include <unistd.h>
-#include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
 #include <fcntl.h>
 #include <ctype.h>
 
 #ifdef __MACH__
 #define CLOCK_MONOTONIC 0
-// clock_gettime is not implemented on OSX
+// clock_gettime is not implemented on OSX (supposedly added on macOS)
 int clock_gettime(int id, struct timespec* t) {
     (void)id;
     struct timeval now;
diff --git a/libx/x11.c b/libx/x11.c
new file mode 100644 (file)
index 0000000..9a51160
--- /dev/null
@@ -0,0 +1,339 @@
+#include <stdc.h>
+#include <X.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <utf.h>
+#include <locale.h>
+
+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 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) {
+    XCloseDisplay(X.display);
+}
+
+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)))
+        assert(false);
+    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);
+}
+
+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]);
+    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
+        | ExposureMask
+        | 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);
+}
+
+static uint32_t getkey(XEvent* e) {
+    uint32_t rune = 0xFFFD;
+    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);
+    /* decode it */
+    if (len > 0) {
+        len = 0;
+        for(int i = 0; i < 8 && !utf8decode(&rune, &len, buf[i]); i++);
+    }
+    /* translate the key code into a unicode codepoint */
+    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;
+        default:           return rune;
+    }
+}
+
+static void handle_mouse(XEvent* e) {
+    MouseAct action;
+    MouseBtn button;
+    int x = 0, y = 0;
+    if (e->type == MotionNotify) {
+        action = MOUSE_ACT_MOVE;
+        button = MOUSE_BTN_LEFT;
+        x      = e->xmotion.x;
+        y      = e->xmotion.y;
+    } else {
+        action = (e->type = ButtonPress ? MOUSE_ACT_DOWN : MOUSE_ACT_UP);
+        /* set the button id */
+        switch (e->xbutton.button) {
+            case Button1: button = MOUSE_BTN_LEFT;      break;
+            case Button2: button = MOUSE_BTN_MIDDLE;    break;
+            case Button3: button = MOUSE_BTN_RIGHT;     break;
+            case Button4: button = MOUSE_BTN_WHEELUP;   break;
+            case Button5: button = MOUSE_BTN_WHEELDOWN; break;
+            default:      button = MOUSE_BTN_NONE;      break;
+        }
+        x = e->xbutton.x;
+        y = e->xbutton.y;
+    }
+    /* pass the data to the app */
+    Config->handle_mouse(action, button, x, y);
+}
+
+void x11_loop(void) {
+    XEvent e;
+    while (true) {
+        XPeekEvent(X.display,&e);
+        while (XPending(X.display)) {
+            XNextEvent(X.display, &e);
+            if (!XFilterEvent(&e, None))
+                switch (e.type) {
+                    case FocusIn:      if (X.xic) XSetICFocus(X.xic);   break;
+                    case FocusOut:     if (X.xic) XUnsetICFocus(X.xic); break;
+                    case KeyPress:     Config->handle_key(getkey(&e));  break;
+                    case ButtonPress:  handle_mouse(&e);                break;
+                    case MotionNotify: handle_mouse(&e);                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;
+                }
+        }
+        /* redraw the window */
+        Config->redraw(X.width, X.height);
+        XCopyArea(X.display, X.pixmap, X.window, X.gc, 0, 0, X.width, X.height, 0, 0);
+        XFlush(X.display);
+    }
+}
+
+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_getsize(int* width, int* height) {
+    *width  = X.width;
+    *height = X.height;
+}
+
+void x11_font_load(XFont* font, char* name) {
+    /* init the library and the base font pattern */
+    if (!FcInit())
+        die("Could not init fontconfig.\n");
+    FcPattern* pattern = FcNameParse((FcChar8 *)name);
+    if (!pattern)
+        die("st: 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);
+}
+
+void x11_font_getglyph(XFont* font, XftGlyphFontSpec* spec, uint32_t rune) {
+    /* 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 >= MAXFONTS) {
+        font->ncached = MAXFONTS - 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);
+}
+
+void x11_draw_glyphs(int fg, int bg, XftGlyphFontSpec* 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);
+        xftcolor(&bgc, Config->palette[bg]);
+        x11_draw_rect(bg, specs[0].x, specs[0].y - h, nspecs * w, font->height);
+        XftColorFree(X.display, X.visual, X.colormap, &bgc);
+    }
+    xftcolor(&fgc, Config->palette[fg]);
+    XftDrawGlyphFontSpec(X.xft, &fgc, specs, nspecs);
+    XftColorFree(X.display, X.visual, X.colormap, &fgc);
+}
+
+size_t x11_font_getglyphs(XftGlyphFontSpec* specs, const XGlyph* glyphs, int len, XFont* font, int x, int y) {
+    int winx = x * font->base.width, winy = y * font->base.ascent;
+    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_warp_mouse(int x, int y) {
+    XWarpPointer(X.display, X.window, X.window, 0, 0, X.width, X.height, x, y);
+}
index 73f688b64c1aaeda864b6f6664d5c048d035ac3e..8c8acee8892b3e692af76eb3b682be419f68ad7a 100644 (file)
@@ -1,5 +1,7 @@
-#include "atf.h"
-#include "edit.h"
+#include <atf.h>
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 
 static Buf TestBuf;
 
index 006954ea018f4cbcf25c922a6a0349ad27b29941..c8d71704a7b2e19af4d7c5dc0bef222af2ab676c 100644 (file)
@@ -1,4 +1,7 @@
-#include "atf.h"
+#include <atf.h>
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 
 TEST_SUITE(Utf8Tests) {
 }
similarity index 83%
rename from tests/tests.c
rename to unittests.c
index d2e8f0a0b24b0e1620abec7ec677d95d9980e205..bbe0552af7a980de023649f955817a226095a28f 100644 (file)
@@ -1,13 +1,14 @@
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
 #define INCLUDE_DEFS
-#include "atf.h"
-#include "edit.h"
+#include <atf.h>
 
 Buf Buffer;
 unsigned CursorPos;
 unsigned TargetCol;
 unsigned SelBeg;
 unsigned SelEnd;
-enum ColorScheme ColorBase;
 
 void move_pointer(unsigned x, unsigned y) {
     (void)x;
diff --git a/xedit.c b/xedit.c
index 4a9755e7e040db99485186655a4d8729d4b9e901..0d9c4913aeb165f6b70f07ebba4f12af4430bf36 100644 (file)
--- a/xedit.c
+++ b/xedit.c
-#define _GNU_SOURCE
-#include <time.h>
-#include <signal.h>
-#include <locale.h>
-#include <X11/Xlib.h>
-#include <X11/Xft/Xft.h>
+#include <stdc.h>
+#include <X.h>
+#include <utf.h>
+#include <edit.h>
 
-#include "edit.h"
+static void redraw(int width, int height);
+static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y);
 
 Buf Buffer;
 unsigned TargetCol = 0;
 unsigned SelBeg = 0;
 unsigned SelEnd = 0;
-enum ColorScheme ColorBase = DEFAULT_COLORSCHEME;
-XftColor Palette[CLR_COUNT][2];
-struct {
-    Display* display;
-    Visual* visual;
-    Colormap colormap;
-    unsigned depth;
-    int screen;
-    GC gc;
-    Window window;
-    Pixmap pixmap;
-    XftDraw* xft;
-    XftFont* font;
-    int width;
-    int height;
-    int rows;
-    int cols;
-    XIC xic;
-    XIM xim;
-} X;
-struct {
-    struct {
-        int height;
-        int width;
-        int ascent;
-        int descent;
-        XftFont* match;
-        FcFontSet* set;
-        FcPattern* pattern;
-    } base;
-    struct {
-        XftFont* font;
-        Rune unicodep;
-    } cache[MaxFonts];
-    int ncached;
-} Fonts;
 
-/*****************************************************************************/
-
-void font_init(void) {
-    /* init the library and the base font pattern */
-    if (!FcInit())
-        die("Could not init fontconfig.\n");
-    FcPattern* pattern = FcNameParse((FcChar8 *)FONTNAME);
-    if (!pattern)
-        die("st: can't open font %s\n", FONTNAME);
-
-    /* load the base font */
-    FcResult result;
-    FcPattern* match = XftFontMatch(X.display, X.screen, pattern, &result);
-    if (!match || !(Fonts.base.match = XftFontOpenPattern(X.display, match)))
-        die("could not load default font: %s", FONTNAME);
-
-    /* get base font extents */
-    XGlyphInfo extents;
-    const FcChar8 ascii[] =
-        " !\"#$%&'()*+,-./0123456789:;<=>?"
-        "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
-        "`abcdefghijklmnopqrstuvwxyz{|}~";
-    XftTextExtentsUtf8(X.display, Fonts.base.match, ascii, sizeof(ascii), &extents);
-    Fonts.base.set     = NULL;
-    Fonts.base.pattern = FcPatternDuplicate(pattern);
-    Fonts.base.ascent  = Fonts.base.match->ascent;
-    Fonts.base.descent = Fonts.base.match->descent;
-    Fonts.base.height  = Fonts.base.ascent + Fonts.base.descent;
-    Fonts.base.width   = ((extents.xOff + (sizeof(ascii) - 1)) / sizeof(ascii));
-    FcPatternDestroy(pattern);
-}
-
-void font_find(XftGlyphFontSpec* spec, Rune rune) {
-    /* if the rune is in the base font, set it and return */
-    FT_UInt glyphidx = XftCharIndex(X.display, Fonts.base.match, rune);
-    if (glyphidx) {
-        spec->font  = Fonts.base.match;
-        spec->glyph = glyphidx;
-        return;
-    }
-    /* Otherwise check the cache */
-    for (int f = 0; f < Fonts.ncached; f++) {
-        glyphidx = XftCharIndex(X.display, Fonts.cache[f].font, rune);
-        /* Fond a suitable font or found a default font */
-        if (glyphidx || (!glyphidx && Fonts.cache[f].unicodep == rune)) {
-            spec->font  = Fonts.cache[f].font;
-            spec->glyph = glyphidx;
-            return;
-        }
-    }
-    /* if all other options fail, ask fontconfig for a suitable font */
-    FcResult fcres;
-    if (!Fonts.base.set)
-        Fonts.base.set = FcFontSort(0, Fonts.base.pattern, 1, 0, &fcres);
-    FcFontSet* fcsets[]  = { Fonts.base.set };
-    FcPattern* fcpattern = FcPatternDuplicate(Fonts.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 (Fonts.ncached >= MaxFonts) {
-        Fonts.ncached = MaxFonts - 1;
-        XftFontClose(X.display, Fonts.cache[Fonts.ncached].font);
-    }
-    Fonts.cache[Fonts.ncached].font = XftFontOpenPattern(X.display, fontmatch);
-    Fonts.cache[Fonts.ncached].unicodep = rune;
-    spec->glyph = XftCharIndex(X.display, Fonts.cache[Fonts.ncached].font, rune);
-    spec->font  = Fonts.cache[Fonts.ncached].font;
-    Fonts.ncached++;
-    FcPatternDestroy(fcpattern);
-    FcCharSetDestroy(fccharset);
-}
-
-int font_makespecs(XftGlyphFontSpec* specs, const UGlyph* glyphs, int len, int x, int y) {
-    int winx = x * Fonts.base.width, winy = y * Fonts.base.ascent;
-    int numspecs = 0;
-    for (int i = 0, xp = winx, yp = winy + Fonts.base.ascent; i < len;) {
-        font_find(&(specs[numspecs]), glyphs[i].rune);
-        specs[numspecs].x = xp;
-        specs[numspecs].y = yp;
-        xp += Fonts.base.width;
-        numspecs++;
-        i++;
-        /* skip over null chars which mark multi column runes */
-        for (; i < len && !glyphs[i].rune; i++)
-            xp += Fonts.base.width;
-    }
-    return numspecs;
-}
-
-/*****************************************************************************/
-
-static void xftcolor(XftColor* xc, Color c) {
-    xc->color.red   = 0xFF | ((c & 0x00FF0000) >> 8);
-    xc->color.green = 0xFF | ((c & 0x0000FF00));
-    xc->color.blue  = 0xFF | ((c & 0x000000FF) << 8);
-    xc->color.alpha = UINT16_MAX;
-    XftColorAllocValue(X.display, X.visual, X.colormap, &(xc->color), xc);
-}
-
-XftColor* clr(int id) {
-    return &(Palette[id][ColorBase]);
-}
-
-static void deinit(void) {
-    if (X.pixmap != None) {
-        XftDrawDestroy(X.xft);
-        XFreePixmap(X.display, X.pixmap);
-    }
-    XCloseDisplay(X.display);
-}
-
-static int init(void) {
-    atexit(deinit);
-    signal(SIGPIPE, SIG_IGN); // Ignore the SIGPIPE signal
-    setlocale(LC_CTYPE, "");
-    XSetLocaleModifiers("");
-    /* open the X display and get basic attributes */
-    if (!(X.display = XOpenDisplay(0)))
-        die("cannot open display");
-    Window root = DefaultRootWindow(X.display);
-    XWindowAttributes wa;
-    XGetWindowAttributes(X.display, root, &wa);
-    X.visual   = wa.visual;
-    X.colormap = wa.colormap;
-    X.screen   = DefaultScreen(X.display);
-    X.depth    = DefaultDepth(X.display, X.screen);
-
-    /* create the main window */
-    X.window = XCreateSimpleWindow(X.display, root, 0, 0, Width, Height, 0, 0, WhitePixel(X.display, X.screen));
-    XSetWindowAttributes swa;
-    swa.backing_store = WhenMapped;
-    swa.bit_gravity = NorthWestGravity;
-    XChangeWindowAttributes(X.display, X.window, CWBackingStore|CWBitGravity, &swa);
-    XStoreName(X.display, X.window, "edit");
-    XSelectInput(X.display, X.window, StructureNotifyMask|ButtonPressMask|ButtonReleaseMask|Button1MotionMask|KeyPressMask|ExposureMask|FocusChangeMask);
-
-    /* simulate an initial resize and map the window */
-    XConfigureEvent ce;
-    ce.type   = ConfigureNotify;
-    ce.width  = Width;
-    ce.height = Height;
-    XSendEvent(X.display, X.window, False, StructureNotifyMask, (XEvent *)&ce);
-    XMapWindow(X.display, X.window);
-
-    /* allocate font */
-    font_init();
-
-    /* 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 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);
-
-    /* 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 color pallette */
-    for (int i = 0; i < CLR_COUNT; i++) {
-        xftcolor(&Palette[i][LIGHT], ColorScheme[i][LIGHT]);
-        xftcolor(&Palette[i][DARK],  ColorScheme[i][DARK]);
-    }
-
-    return XConnectionNumber(X.display);
-}
-
-static Rune getkey(XEvent* e) {
-    Rune 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);
-    /* decode it */
-    if (len > 0) {
-        len = 0;
-        for(int i = 0; i < 8 && !utf8decode(&rune, &len, buf[i]); i++);
-    }
-    /* translate the key code into a unicode codepoint */
-    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;
-        default:           return rune;
-    }
-}
-
-static MouseEvent* getmouse(XEvent* e) {
-    static MouseEvent event;
-    if (e->type == MotionNotify) {
-        event.type   = MouseMove;
-        event.button = MOUSE_LEFT;
-        event.x      = e->xmotion.x / Fonts.base.width;
-        event.y      = e->xmotion.y / Fonts.base.height;
-    } else {
-        event.type = (e->type = ButtonPress ? MouseDown : MouseUp);
-        /* set the button id */
-        switch (e->xbutton.button) {
-            case Button1: event.button = MOUSE_LEFT;      break;
-            case Button2: event.button = MOUSE_MIDDLE;    break;
-            case Button3: event.button = MOUSE_RIGHT;     break;
-            case Button4: event.button = MOUSE_WHEELUP;   break;
-            case Button5: event.button = MOUSE_WHEELDOWN; break;
-            default:      event.button = MOUSE_NONE;      break;
-        }
-        event.x = e->xbutton.x / Fonts.base.width;
-        event.y = e->xbutton.y / Fonts.base.height;
-    }
-    return &event;
-}
-
-static void handle_event(XEvent* e) {
-    switch (e->type) {
-        case FocusIn:      if (X.xic) XSetICFocus(X.xic);   break;
-        case FocusOut:     if (X.xic) XUnsetICFocus(X.xic); break;
-        case KeyPress:     handle_key(getkey(e));           break;
-        case ButtonPress:  handle_mouse(getmouse(e));       break;
-        case MotionNotify: handle_mouse(getmouse(e));       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);
-                screen_setsize(
-                    &Buffer,
-                    X.height / Fonts.base.height - 1,
-                    X.width  / Fonts.base.width);
-            }
-            break;
-    }
-}
-
-void draw_runes(unsigned x, unsigned y, XftColor* fg, XftColor* bg, UGlyph* glyphs, size_t rlen) {
-    (void)bg;
+static XFont Fonts;
+static XConfig Config = {
+    .redraw       = redraw,
+    .handle_key   = handle_key,
+    .handle_mouse = mouse_handler,
+    .palette    = {
+        /* ARGB color values */
+        0xff002b36,
+        0xff073642,
+        0xff586e75,
+        0xff657b83,
+        0xff839496,
+        0xff93a1a1,
+        0xffeee8d5,
+        0xfffdf6e3,
+        0xffb58900,
+        0xffcb4b16,
+        0xffdc322f,
+        0xffd33682,
+        0xff6c71c4,
+        0xff268bd2,
+        0xff2aa198,
+        0xff859900
+    }
+};
+
+static void draw_runes(unsigned x, unsigned y, int fg, int bg, UGlyph* glyphs, size_t rlen) {
     XftGlyphFontSpec specs[rlen];
     while (rlen) {
-        size_t nspecs = font_makespecs(specs, glyphs, rlen, x, y);
-        XftDrawGlyphFontSpec(X.xft, fg, specs, nspecs);
+        size_t nspecs = x11_font_getglyphs(specs, (XGlyph*)glyphs, rlen, &Fonts, x, y);
+        x11_draw_glyphs(fg, bg, specs, nspecs);
         rlen -= nspecs;
     }
 }
 
-void draw_glyphs(unsigned x, unsigned y, UGlyph* glyphs, size_t rlen, size_t ncols) {
+static void draw_glyphs(unsigned x, unsigned y, UGlyph* glyphs, size_t rlen, size_t ncols) {
     XftGlyphFontSpec specs[rlen];
     size_t i = 0;
     while (rlen) {
         int numspecs = 0;
         uint32_t attr = glyphs[i].attr;
         while (i < ncols && glyphs[i].attr == attr) {
-            font_find(&(specs[numspecs]), glyphs[i].rune);
+            x11_font_getglyph(&Fonts, &(specs[numspecs]), glyphs[i].rune);
             specs[numspecs].x = x;
             specs[numspecs].y = y - Fonts.base.descent;
             x += Fonts.base.width;
@@ -343,24 +66,18 @@ void draw_glyphs(unsigned x, unsigned y, UGlyph* glyphs, size_t rlen, size_t nco
         /* Draw the glyphs with the proper colors */
         uint8_t bg = attr >> 8;
         uint8_t fg = attr & 0xFF;
-        if (bg != CLR_BASE03)
-            XftDrawRect(X.xft, clr(bg), specs[0].x, y-Fonts.base.height, x-specs[0].x, Fonts.base.height);
-        XftDrawGlyphFontSpec(X.xft, clr(fg), specs, numspecs);
+        x11_draw_glyphs(fg, bg, specs, numspecs);
         rlen -= numspecs;
     }
 }
 
-void draw_row(unsigned x, unsigned y, Row* row) {
-    draw_glyphs(x, y, row->cols, row->rlen, row->len);
-}
-
-static void draw_status(XftColor* fg, unsigned ncols) {
+static void draw_status(int fg, unsigned ncols) {
     UGlyph glyphs[ncols], *status = glyphs;
     (status++)->rune = (Buffer.charset == BINARY ? 'B' : 'U');
     (status++)->rune = (Buffer.crlf ? 'C' : 'N');
     (status++)->rune = (Buffer.modified ? '*' : ' ');
     (status++)->rune = ' ';
-    char* path = Buffer.path;
+    char* path = (Buffer.path ? Buffer.path : "*scratch*");
     size_t len = strlen(path);
     if (len > ncols-4) {
         (status++)->rune = '.';
@@ -370,7 +87,7 @@ static void draw_status(XftColor* fg, unsigned ncols) {
     }
     while(*path)
         (status++)->rune = *path++;
-    draw_runes(0, 0, fg, NULL, glyphs, status - glyphs);
+    draw_runes(0, 0, fg, 0, glyphs, status - glyphs);
 }
 
 static void draw_cursor(unsigned csrx, unsigned csry) {
@@ -378,20 +95,24 @@ static void draw_cursor(unsigned csrx, unsigned csry) {
     UGlyph* csrrune = screen_getglyph(csry, csrx, &rwidth);
     csrrune->attr = (CLR_BASE3 << 8 | CLR_BASE03);
     if (buf_locked(&Buffer)) {
-        XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, rwidth * Fonts.base.width, Fonts.base.height);
+        x11_draw_rect(CLR_BASE3, csrx * Fonts.base.width, (csry+1) * Fonts.base.height, rwidth * Fonts.base.width, Fonts.base.height);
         draw_glyphs(csrx * Fonts.base.width, (csry+2) * Fonts.base.height, csrrune, 1, rwidth);
     } else {
-        XftDrawRect(X.xft, clr(CLR_BASE3), csrx * Fonts.base.width, (csry+1) * Fonts.base.height, 1, Fonts.base.height);
+        x11_draw_rect(CLR_BASE3, csrx * Fonts.base.width, (csry+1) * Fonts.base.height, 1, Fonts.base.height);
     }
 }
 
-static void redraw(void) {
-    //uint32_t start = getmillis();
+static void redraw(int width, int height) {
+    /* update the screen size if needed */
+    screen_setsize(&Buffer,
+        height / Fonts.base.height - 1,
+        width  / Fonts.base.width);
+
     /* draw the background colors */
-    XftDrawRect(X.xft, clr(CLR_BASE03), 0, 0, X.width, X.height);
-    XftDrawRect(X.xft, clr(CLR_BASE02), 79 * Fonts.base.width, 0, Fonts.base.width, X.height);
-    XftDrawRect(X.xft, clr(CLR_BASE02), 0, 0, X.width, Fonts.base.height);
-    XftDrawRect(X.xft, clr(CLR_BASE01), 0, Fonts.base.height, X.width, 1);
+    x11_draw_rect(CLR_BASE03, 0, 0, width, height);
+    x11_draw_rect(CLR_BASE02, 79 * Fonts.base.width, 0, Fonts.base.width, height);
+    x11_draw_rect(CLR_BASE02, 0, 0, width, Fonts.base.height);
+    x11_draw_rect(CLR_BASE01, 0, Fonts.base.height, width, 1);
 
     /* update the screen buffer and retrieve cursor coordinates */
     unsigned csrx, csry;
@@ -400,7 +121,7 @@ static void redraw(void) {
     /* flush the screen buffer */
     unsigned nrows, ncols;
     screen_getsize(&nrows, &ncols);
-    draw_status(clr(CLR_BASE2), ncols);
+    draw_status(CLR_BASE2, ncols);
     for (unsigned y = 0; y < nrows; y++) {
         Row* row = screen_getrow(y);
         draw_glyphs(0, (y+2) * Fonts.base.height, row->cols, row->rlen, row->len);
@@ -408,11 +129,16 @@ static void redraw(void) {
 
     /* Place cursor on screen */
     draw_cursor(csrx, csry);
+}
 
-    /* flush pixels to the screen */
-    XCopyArea(X.display, X.pixmap, X.window, X.gc, 0, 0, X.width, X.height, 0, 0);
-    XFlush(X.display);
-    //printf("refresh: %u\n", getmillis() - start);
+static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y) {
+    MouseEvent evnt = {
+        .type = act,
+        .button = btn,
+        .x = x / Fonts.base.width,
+        .y = y / Fonts.base.height
+    };
+    handle_mouse(&evnt);
 }
 
 int main(int argc, char** argv) {
@@ -421,23 +147,16 @@ int main(int argc, char** argv) {
     if (argc > 1)
         buf_load(&Buffer, argv[1]);
     /* initialize the display engine */
-    init();
-    /* main x11 event loop */
-    XEvent e;
-    while (true) {
-        XPeekEvent(X.display,&e);
-        while (XPending(X.display)) {
-            XNextEvent(X.display, &e);
-            if (!XFilterEvent(&e, None))
-                handle_event(&e);
-        }
-        redraw();
-    }
+    x11_init(&Config);
+    x11_window("edit", Width, Height);
+    x11_show();
+    x11_font_load(&Fonts, FONTNAME);
+    x11_loop();
     return 0;
 }
 
 void move_pointer(unsigned x, unsigned y) {
     x = (x * Fonts.base.width)  + (Fonts.base.width / 2);
     y = ((y+1) * Fonts.base.height) + (Fonts.base.height / 2);
-    XWarpPointer(X.display, X.window, X.window, 0, 0, X.width, X.height, x, y);
+    x11_warp_mouse(x,y);
 }
diff --git a/xpick.c b/xpick.c
new file mode 100644 (file)
index 0000000..9f3ef7d
--- /dev/null
+++ b/xpick.c
@@ -0,0 +1,66 @@
+#include <stdc.h>
+#include <X.h>
+#include <utf.h>
+#include <edit.h>
+
+static void redraw(int width, int height);
+static void mouse_input(MouseAct act, MouseBtn btn, int x, int y);
+static void keyboard_input(uint32_t key);
+
+Buf Buffer;
+static XFont Fonts;
+static XConfig Config = {
+    .redraw       = redraw,
+    .handle_key   = keyboard_input,
+    .handle_mouse = mouse_input,
+    .palette    = {
+        /* ARGB color values */
+        0xff002b36,
+        0xff073642,
+        0xff586e75,
+        0xff657b83,
+        0xff839496,
+        0xff93a1a1,
+        0xffeee8d5,
+        0xfffdf6e3,
+        0xffb58900,
+        0xffcb4b16,
+        0xffdc322f,
+        0xffd33682,
+        0xff6c71c4,
+        0xff268bd2,
+        0xff2aa198,
+        0xff859900
+    }
+};
+
+static void redraw(int width, int height) {
+    /* draw the background colors */
+    x11_draw_rect(CLR_BASE03, 0, 0, width, height);
+    x11_draw_rect(CLR_BASE02, 0, 0, width, Fonts.base.height);
+    x11_draw_rect(CLR_BASE01, 0, Fonts.base.height, width, 1);
+}
+
+static void mouse_input(MouseAct act, MouseBtn btn, int x, int y) {
+    (void)act;
+    (void)btn;
+    (void)x;
+    (void)y;
+}
+
+static void keyboard_input(uint32_t key) {
+    (void)key;
+}
+
+int main(int argc, char** argv) {
+    /* load the buffer */
+    buf_init(&Buffer);
+    buf_setlocked(&Buffer, false);
+    /* initialize the display engine */
+    x11_init(&Config);
+    x11_window("pick", Width, Height);
+    x11_show();
+    x11_font_load(&Fonts, FONTNAME);
+    x11_loop();
+    return 0;
+}