From 3d87abf5e5c7cd1867c674ad3bd1cad7b819f936 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Thu, 3 Nov 2016 20:01:15 -0400 Subject: [PATCH] major reorginization to allow reuse of gui and edit buffer code --- Makefile | 53 ++-- inc/X.h | 136 ++++++++++ {tests => inc}/atf.h | 0 edit.h => inc/edit.h | 133 ---------- inc/stdc.h | 200 +++++++++++++++ inc/utf.h | 15 ++ buf.c => libedit/buf.c | 9 +- charset.c => libedit/charset.c | 4 +- keyboard.c => libedit/keyboard.c | 11 +- mouse.c => libedit/mouse.c | 5 +- screen.c => libedit/screen.c | 5 +- utf8.c => libedit/utf8.c | 10 +- utils.c => libedit/utils.c | 12 +- libx/x11.c | 339 +++++++++++++++++++++++++ tests/buf.c | 6 +- tests/utf8.c | 5 +- tests/tests.c => unittests.c | 7 +- xedit.c | 419 +++++-------------------------- xpick.c | 66 +++++ 19 files changed, 893 insertions(+), 542 deletions(-) create mode 100644 inc/X.h rename {tests => inc}/atf.h (100%) rename edit.h => inc/edit.h (57%) create mode 100644 inc/stdc.h create mode 100644 inc/utf.h rename buf.c => libedit/buf.c (99%) rename charset.c => libedit/charset.c (97%) rename keyboard.c => libedit/keyboard.c (97%) rename mouse.c => libedit/mouse.c (98%) rename screen.c => libedit/screen.c (98%) rename utf8.c => libedit/utf8.c (95%) rename utils.c => libedit/utils.c (92%) create mode 100644 libx/x11.c rename tests/tests.c => unittests.c (83%) create mode 100644 xpick.c diff --git a/Makefile b/Makefile index dcb9697..7a305fd 100644 --- 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 index 0000000..0301c94 --- /dev/null +++ b/inc/X.h @@ -0,0 +1,136 @@ +#include +#include + +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); diff --git a/tests/atf.h b/inc/atf.h similarity index 100% rename from tests/atf.h rename to inc/atf.h diff --git a/edit.h b/inc/edit.h similarity index 57% rename from edit.h rename to inc/edit.h index 576f813..ef1645d 100644 --- a/edit.h +++ b/inc/edit.h @@ -1,30 +1,3 @@ -#include -#include -#include -#include -#include -#include - -/* 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 index 0000000..3a12993 --- /dev/null +++ b/inc/stdc.h @@ -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 +#include +#include +#include +#include +#include + +/* Useful Standard Functions */ +#include +#include +#include +#include +#include + +/* 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 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); diff --git a/buf.c b/libedit/buf.c similarity index 99% rename from buf.c rename to libedit/buf.c index 9cd099c..63f257d 100644 --- a/buf.c +++ b/libedit/buf.c @@ -1,9 +1,6 @@ -#define _GNU_SOURCE -#include -#include -#include - -#include "edit.h" +#include +#include +#include void buf_load(Buf* buf, char* path) { buf_setlocked(buf, false); diff --git a/charset.c b/libedit/charset.c similarity index 97% rename from charset.c rename to libedit/charset.c index 5ef1e07..6051db1 100644 --- a/charset.c +++ b/libedit/charset.c @@ -1,4 +1,6 @@ -#include "edit.h" +#include +#include +#include static const struct { int type; diff --git a/keyboard.c b/libedit/keyboard.c similarity index 97% rename from keyboard.c rename to libedit/keyboard.c index 59df3a2..c05df34 100644 --- a/keyboard.c +++ b/libedit/keyboard.c @@ -1,8 +1,7 @@ -#include "edit.h" - -static void toggle_colors(void) { - ColorBase = !ColorBase; -} +#include +#include +#include +#include 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 }, diff --git a/mouse.c b/libedit/mouse.c similarity index 98% rename from mouse.c rename to libedit/mouse.c index f623917..81a5086 100644 --- a/mouse.c +++ b/libedit/mouse.c @@ -1,5 +1,6 @@ -#include "edit.h" -#include +#include +#include +#include void unused(MouseEvent* mevnt) { (void)mevnt; diff --git a/screen.c b/libedit/screen.c similarity index 98% rename from screen.c rename to libedit/screen.c index 5cd464b..cd1c3ae 100644 --- a/screen.c +++ b/libedit/screen.c @@ -1,4 +1,6 @@ -#include "edit.h" +#include +#include +#include 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++) diff --git a/utf8.c b/libedit/utf8.c similarity index 95% rename from utf8.c rename to libedit/utf8.c index 824da09..c96a7a6 100644 --- a/utf8.c +++ b/libedit/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 +#include +#include #include const uint8_t UTF8_SeqBits[] = { 0x00u, 0x80u, 0xC0u, 0xE0u, 0xF0u, 0xF8u, 0xFCu, 0xFEu }; diff --git a/utils.c b/libedit/utils.c similarity index 92% rename from utils.c rename to libedit/utils.c index 5fd2278..7003c33 100644 --- a/utils.c +++ b/libedit/utils.c @@ -1,17 +1,17 @@ -#include "edit.h" - -#define _GNU_SOURCE +#include +#include +#include #include #include -#include -#include #include +#include +#include #include #include #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 index 0000000..9a51160 --- /dev/null +++ b/libx/x11.c @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include +#include + +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); +} diff --git a/tests/buf.c b/tests/buf.c index 73f688b..8c8acee 100644 --- a/tests/buf.c +++ b/tests/buf.c @@ -1,5 +1,7 @@ -#include "atf.h" -#include "edit.h" +#include +#include +#include +#include static Buf TestBuf; diff --git a/tests/utf8.c b/tests/utf8.c index 006954e..c8d7170 100644 --- a/tests/utf8.c +++ b/tests/utf8.c @@ -1,4 +1,7 @@ -#include "atf.h" +#include +#include +#include +#include TEST_SUITE(Utf8Tests) { } diff --git a/tests/tests.c b/unittests.c similarity index 83% rename from tests/tests.c rename to unittests.c index d2e8f0a..bbe0552 100644 --- a/tests/tests.c +++ b/unittests.c @@ -1,13 +1,14 @@ +#include +#include +#include #define INCLUDE_DEFS -#include "atf.h" -#include "edit.h" +#include 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 4a9755e..0d9c491 100644 --- a/xedit.c +++ b/xedit.c @@ -1,336 +1,59 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include +#include +#include +#include +#include -#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 index 0000000..9f3ef7d --- /dev/null +++ b/xpick.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +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; +} -- 2.49.0