-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
--- /dev/null
+#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);
-#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 {
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);
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);
/* 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 {
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;
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 */
--- /dev/null
+/**
+ @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
--- /dev/null
+#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);
-#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);
-#include "edit.h"
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
static const struct {
int type;
-#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);
} KeyBinding_T;
static KeyBinding_T Normal[] = {
- { KEY_F6, toggle_colors },
{ KEY_CTRL_Q, quit },
{ KEY_CTRL_W, write },
//{ 'q', quit },
};
static KeyBinding_T Insert[] = {
- { KEY_F6, toggle_colors },
{ KEY_UP, cursor_up },
{ KEY_DOWN, cursor_dn },
{ KEY_LEFT, cursor_left },
-#include "edit.h"
-#include <ctype.h>
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
void unused(MouseEvent* mevnt) {
(void)mevnt;
-#include "edit.h"
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
static unsigned NumRows = 0;
static unsigned NumCols = 0;
}
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++)
-/**
- @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 };
-#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;
--- /dev/null
+#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);
+}
-#include "atf.h"
-#include "edit.h"
+#include <atf.h>
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
static Buf TestBuf;
-#include "atf.h"
+#include <atf.h>
+#include <stdc.h>
+#include <utf.h>
+#include <edit.h>
TEST_SUITE(Utf8Tests) {
}
+#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;
-#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;
/* 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 = '.';
}
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) {
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;
/* 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);
/* 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) {
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);
}
--- /dev/null
+#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;
+}