selection clicks, shift+arrow keys, fast(er) redraw, key mask in config.h (thx Magnus Leuthner)
This commit is contained in:
		
							parent
							
								
									d3c5aba2d9
								
							
						
					
					
						commit
						9d5ea14b9d
					
				
							
								
								
									
										45
									
								
								config.def.h
								
								
								
								
							
							
						
						
									
										45
									
								
								config.def.h
								
								
								
								
							| 
						 | 
				
			
			@ -31,27 +31,28 @@ static const char *colorname[] = {
 | 
			
		|||
#define DefaultBG 0
 | 
			
		||||
#define DefaultCS 1
 | 
			
		||||
 | 
			
		||||
/* Special keys */
 | 
			
		||||
/* Special keys (change & recompile st.info accordingly) */
 | 
			
		||||
/*    key,        mask,  output */
 | 
			
		||||
static Key key[] = {
 | 
			
		||||
	{ XK_BackSpace, "\177" },
 | 
			
		||||
	{ XK_Insert,    "\033[2~" },
 | 
			
		||||
	{ XK_Delete,    "\033[3~" },
 | 
			
		||||
	{ XK_Home,      "\033[1~" },
 | 
			
		||||
	{ XK_End,       "\033[4~" },
 | 
			
		||||
	{ XK_Prior,     "\033[5~" },
 | 
			
		||||
	{ XK_Next,      "\033[6~" },
 | 
			
		||||
	{ XK_F1,        "\033OP"   },
 | 
			
		||||
	{ XK_F2,        "\033OQ"   },
 | 
			
		||||
	{ XK_F3,        "\033OR"   },
 | 
			
		||||
	{ XK_F4,        "\033OS"   },
 | 
			
		||||
	{ XK_F5,        "\033[15~" },
 | 
			
		||||
	{ XK_F6,        "\033[17~" },
 | 
			
		||||
	{ XK_F7,        "\033[18~" },
 | 
			
		||||
	{ XK_F8,        "\033[19~" },
 | 
			
		||||
	{ XK_F9,        "\033[20~" },
 | 
			
		||||
	{ XK_F10,       "\033[21~" },
 | 
			
		||||
	{ XK_F11,       "\033[23~" },
 | 
			
		||||
	{ XK_F12,       "\033[24~" },
 | 
			
		||||
	{ XK_BackSpace, 0, "\177" },
 | 
			
		||||
	{ XK_Insert,    0, "\033[2~" },
 | 
			
		||||
	{ XK_Delete,    0, "\033[3~" },
 | 
			
		||||
	{ XK_Home,      0, "\033[1~" },
 | 
			
		||||
	{ XK_End,       0, "\033[4~" },
 | 
			
		||||
	{ XK_Prior,     0, "\033[5~" },
 | 
			
		||||
	{ XK_Next,      0, "\033[6~" },
 | 
			
		||||
	{ XK_F1,        0, "\033OP"   },
 | 
			
		||||
	{ XK_F2,        0, "\033OQ"   },
 | 
			
		||||
	{ XK_F3,        0, "\033OR"   },
 | 
			
		||||
	{ XK_F4,        0, "\033OS"   },
 | 
			
		||||
	{ XK_F5,        0, "\033[15~" },
 | 
			
		||||
	{ XK_F6,        0, "\033[17~" },
 | 
			
		||||
	{ XK_F7,        0, "\033[18~" },
 | 
			
		||||
	{ XK_F8,        0, "\033[19~" },
 | 
			
		||||
	{ XK_F9,        0, "\033[20~" },
 | 
			
		||||
	{ XK_F10,       0, "\033[21~" },
 | 
			
		||||
	{ XK_F11,       0, "\033[23~" },
 | 
			
		||||
	{ XK_F12,       0, "\033[24~" },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Line drawing characters (sometime specific to each font...) */
 | 
			
		||||
| 
						 | 
				
			
			@ -61,3 +62,7 @@ static char gfx[] = {
 | 
			
		|||
	['i'] = '#',
 | 
			
		||||
	[255] = 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* double-click timeout (in milliseconds) between clicks for selection */
 | 
			
		||||
#define DOUBLECLICK_TIMEOUT 300
 | 
			
		||||
#define TRIPLECLICK_TIMEOUT (2*DOUBLECLICK_TIMEOUT)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										100
									
								
								st.c
								
								
								
								
							
							
						
						
									
										100
									
								
								st.c
								
								
								
								
							| 
						 | 
				
			
			@ -22,6 +22,9 @@
 | 
			
		|||
#include <X11/cursorfont.h>
 | 
			
		||||
#include <X11/keysym.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
 | 
			
		||||
#if   defined(__linux)
 | 
			
		||||
 #include <pty.h>
 | 
			
		||||
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +53,7 @@
 | 
			
		|||
#define LIMIT(x, a, b)    (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
 | 
			
		||||
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg)
 | 
			
		||||
#define IS_SET(flag) (term.mode & (flag))
 | 
			
		||||
#define TIMEDIFFERENCE(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000)
 | 
			
		||||
 | 
			
		||||
/* Attribute, Cursor, Character state, Terminal mode, Screen draw mode */
 | 
			
		||||
enum { ATTR_NULL=0 , ATTR_REVERSE=1 , ATTR_UNDERLINE=2, ATTR_BOLD=4, ATTR_GFX=8 };
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +64,6 @@ enum { GLYPH_SET=1, GLYPH_DIRTY=2 };
 | 
			
		|||
enum { MODE_WRAP=1, MODE_INSERT=2, MODE_APPKEYPAD=4, MODE_ALTSCREEN=8,
 | 
			
		||||
       MODE_CRLF=16 };
 | 
			
		||||
enum { ESC_START=1, ESC_CSI=2, ESC_OSC=4, ESC_TITLE=8, ESC_ALTCHARSET=16 };
 | 
			
		||||
enum { SCREEN_UPDATE, SCREEN_REDRAW };
 | 
			
		||||
enum { WIN_VISIBLE=1, WIN_REDRAW=2, WIN_FOCUSED=4 };
 | 
			
		||||
 | 
			
		||||
#undef B0
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +132,7 @@ typedef struct {
 | 
			
		|||
 | 
			
		||||
typedef struct {
 | 
			
		||||
	KeySym k;
 | 
			
		||||
	unsigned int mask;
 | 
			
		||||
	char s[ESC_BUF_SIZ];
 | 
			
		||||
} Key;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,12 +157,15 @@ typedef struct {
 | 
			
		|||
	struct {int x, y;} b, e;
 | 
			
		||||
	char *clip;
 | 
			
		||||
	Atom xtarget;
 | 
			
		||||
	struct timeval tclick1;
 | 
			
		||||
	struct timeval tclick2;
 | 
			
		||||
} Selection;
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
 | 
			
		||||
static void die(const char *errstr, ...);
 | 
			
		||||
static void draw(int);
 | 
			
		||||
static void draw();
 | 
			
		||||
static void drawregion(int, int, int, int);
 | 
			
		||||
static void execsh(void);
 | 
			
		||||
static void sigchld(int);
 | 
			
		||||
static void run(void);
 | 
			
		||||
| 
						 | 
				
			
			@ -206,7 +213,7 @@ static void xresize(int, int);
 | 
			
		|||
static void expose(XEvent *);
 | 
			
		||||
static void visibility(XEvent *);
 | 
			
		||||
static void unmap(XEvent *);
 | 
			
		||||
static char* kmap(KeySym);
 | 
			
		||||
static char* kmap(KeySym, unsigned int state);
 | 
			
		||||
static void kpress(XEvent *);
 | 
			
		||||
static void resize(XEvent *);
 | 
			
		||||
static void focus(XEvent *);
 | 
			
		||||
| 
						 | 
				
			
			@ -219,7 +226,7 @@ static void selrequest(XEvent *);
 | 
			
		|||
static void selinit(void);
 | 
			
		||||
static inline int selected(int, int);
 | 
			
		||||
static void selcopy(void);
 | 
			
		||||
static void selpaste(void);
 | 
			
		||||
static void selpaste();
 | 
			
		||||
 | 
			
		||||
static int utf8decode(char *, long *);
 | 
			
		||||
static int utf8encode(long *, char *);
 | 
			
		||||
| 
						 | 
				
			
			@ -368,6 +375,8 @@ utf8size(char *s) {
 | 
			
		|||
 | 
			
		||||
void
 | 
			
		||||
selinit(void) {
 | 
			
		||||
	sel.tclick1.tv_sec = 0;
 | 
			
		||||
	sel.tclick1.tv_usec = 0;
 | 
			
		||||
	sel.mode = 0;
 | 
			
		||||
	sel.bx = -1;
 | 
			
		||||
	sel.clip = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -511,30 +520,63 @@ xsetsel(char *str) {
 | 
			
		|||
	XFlush(xw.dpy);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: doubleclick to select word */
 | 
			
		||||
void
 | 
			
		||||
brelease(XEvent *e) {
 | 
			
		||||
	int b;
 | 
			
		||||
	sel.mode = 0;
 | 
			
		||||
	getbuttoninfo(e, &b, &sel.ex, &sel.ey);
 | 
			
		||||
	
 | 
			
		||||
	if(sel.bx == sel.ex && sel.by == sel.ey) {
 | 
			
		||||
		sel.bx = -1;
 | 
			
		||||
		if(b == 2)
 | 
			
		||||
			selpaste();
 | 
			
		||||
 | 
			
		||||
		else if(b == 1) {
 | 
			
		||||
			/* double click to select word */
 | 
			
		||||
			struct timeval now;
 | 
			
		||||
			gettimeofday(&now, NULL);
 | 
			
		||||
 | 
			
		||||
			if(TIMEDIFFERENCE(now, sel.tclick1) <= DOUBLECLICK_TIMEOUT) {
 | 
			
		||||
				sel.bx = sel.ex;
 | 
			
		||||
				while(term.line[sel.ey][sel.bx-1].state & GLYPH_SET && 
 | 
			
		||||
					  term.line[sel.ey][sel.bx-1].c[0] != ' ') sel.bx--;
 | 
			
		||||
				sel.b.x = sel.bx;
 | 
			
		||||
				while(term.line[sel.ey][sel.ex+1].state & GLYPH_SET && 
 | 
			
		||||
					  term.line[sel.ey][sel.ex+1].c[0] != ' ') sel.ex++;
 | 
			
		||||
				sel.e.x = sel.ex;
 | 
			
		||||
				sel.b.y = sel.e.y = sel.ey;
 | 
			
		||||
				selcopy();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* triple click on the line */
 | 
			
		||||
			if(TIMEDIFFERENCE(now, sel.tclick2) <= TRIPLECLICK_TIMEOUT) {
 | 
			
		||||
				sel.b.x = sel.bx = 0;
 | 
			
		||||
				sel.e.x = sel.ex = term.col;
 | 
			
		||||
				sel.b.y = sel.e.y = sel.ey;
 | 
			
		||||
				selcopy();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if(b == 1) 
 | 
			
		||||
			selcopy();
 | 
			
		||||
	}
 | 
			
		||||
	draw(1);
 | 
			
		||||
	memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval));
 | 
			
		||||
	gettimeofday(&sel.tclick1, NULL);
 | 
			
		||||
	draw();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
bmotion(XEvent *e) {
 | 
			
		||||
	if(sel.mode) {
 | 
			
		||||
		int oldey = sel.ey, 
 | 
			
		||||
			oldex = sel.ex;
 | 
			
		||||
		getbuttoninfo(e, NULL, &sel.ex, &sel.ey);
 | 
			
		||||
		/* XXX: draw() can't keep up, disabled for now.
 | 
			
		||||
		   selection is visible on button release.
 | 
			
		||||
		   draw(1); */
 | 
			
		||||
 | 
			
		||||
		if(oldey != sel.ey || oldex != sel.ex) {
 | 
			
		||||
			int starty = MIN(oldey, sel.ey);
 | 
			
		||||
			int endy = MAX(oldey, sel.ey);
 | 
			
		||||
			drawregion(0, (starty > 0 ? starty : 0), term.col, (sel.ey < term.row ? endy+1 : term.row));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -641,6 +683,10 @@ ttyread(void) {
 | 
			
		|||
 | 
			
		||||
void
 | 
			
		||||
ttywrite(const char *s, size_t n) {
 | 
			
		||||
	{size_t nn;
 | 
			
		||||
		for(nn = 0; nn < n; nn++)
 | 
			
		||||
			dump(s[nn]);
 | 
			
		||||
	}
 | 
			
		||||
	if(write(cmdfd, s, n) == -1)
 | 
			
		||||
		die("write error on tty: %s\n", SERRNO);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1641,7 +1687,12 @@ xdrawc(int x, int y, Glyph g) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void 
 | 
			
		||||
draw(int dummy) {
 | 
			
		||||
drawregion(int x0, int x1, int y0, int y1) {
 | 
			
		||||
	draw();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
draw() {
 | 
			
		||||
	int x, y;
 | 
			
		||||
 | 
			
		||||
	xclear(0, 0, term.col-1, term.row-1);
 | 
			
		||||
| 
						 | 
				
			
			@ -1658,7 +1709,12 @@ draw(int dummy) {
 | 
			
		|||
#else
 | 
			
		||||
/* optimized drawing routine */
 | 
			
		||||
void 
 | 
			
		||||
draw(int redraw_all) {
 | 
			
		||||
draw() {
 | 
			
		||||
	drawregion(0, 0, term.col, term.row);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
drawregion(int x1, int y1, int x2, int y2) {
 | 
			
		||||
	int ic, ib, x, y, ox, sl;
 | 
			
		||||
	Glyph base, new;
 | 
			
		||||
	char buf[DRAW_BUF_SIZ];
 | 
			
		||||
| 
						 | 
				
			
			@ -1666,11 +1722,11 @@ draw(int redraw_all) {
 | 
			
		|||
	if(!(xw.state & WIN_VISIBLE))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	xclear(0, 0, term.col-1, term.row-1);
 | 
			
		||||
	for(y = 0; y < term.row; y++) {
 | 
			
		||||
	xclear(x1, y1, x2-1, y2-1);
 | 
			
		||||
	for(y = y1; y < y2; y++) {
 | 
			
		||||
		base = term.line[y][0];
 | 
			
		||||
		ic = ib = ox = 0;
 | 
			
		||||
		for(x = 0; x < term.col; x++) {
 | 
			
		||||
		for(x = x1; x < x2; x++) {
 | 
			
		||||
			new = term.line[y][x];
 | 
			
		||||
			if(sel.bx != -1 && *(new.c) && selected(x, y))
 | 
			
		||||
				new.mode ^= ATTR_REVERSE;
 | 
			
		||||
| 
						 | 
				
			
			@ -1705,7 +1761,7 @@ expose(XEvent *ev) {
 | 
			
		|||
	if(xw.state & WIN_REDRAW) {
 | 
			
		||||
		if(!e->count) {
 | 
			
		||||
			xw.state &= ~WIN_REDRAW;
 | 
			
		||||
			draw(SCREEN_REDRAW);
 | 
			
		||||
			draw();
 | 
			
		||||
		}
 | 
			
		||||
	} else
 | 
			
		||||
		XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, e->x-BORDER, e->y-BORDER,
 | 
			
		||||
| 
						 | 
				
			
			@ -1742,14 +1798,14 @@ focus(XEvent *ev) {
 | 
			
		|||
		xseturgency(0);
 | 
			
		||||
	} else
 | 
			
		||||
		xw.state &= ~WIN_FOCUSED;
 | 
			
		||||
	draw(SCREEN_UPDATE);
 | 
			
		||||
	draw();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char*
 | 
			
		||||
kmap(KeySym k) {
 | 
			
		||||
kmap(KeySym k, unsigned int state) {
 | 
			
		||||
	int i;
 | 
			
		||||
	for(i = 0; i < LEN(key); i++)
 | 
			
		||||
		if(key[i].k == k)
 | 
			
		||||
		if(key[i].k == k && (key[i].mask == 0 || key[i].mask & state))
 | 
			
		||||
			return (char*)key[i].s;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1770,7 +1826,7 @@ kpress(XEvent *ev) {
 | 
			
		|||
	len = XmbLookupString(xw.xic, e, buf, sizeof(buf), &ksym, &status);
 | 
			
		||||
	
 | 
			
		||||
	/* 1. custom keys from config.h */
 | 
			
		||||
	if((customkey = kmap(ksym)))
 | 
			
		||||
	if((customkey = kmap(ksym, e->state)))
 | 
			
		||||
		ttywrite(customkey, strlen(customkey));
 | 
			
		||||
	/* 2. hardcoded (overrides X lookup) */
 | 
			
		||||
	else
 | 
			
		||||
| 
						 | 
				
			
			@ -1779,7 +1835,7 @@ kpress(XEvent *ev) {
 | 
			
		|||
		case XK_Down:
 | 
			
		||||
		case XK_Left:
 | 
			
		||||
		case XK_Right:
 | 
			
		||||
			sprintf(buf, "\033%c%c", IS_SET(MODE_APPKEYPAD) ? 'O' : '[', "DACB"[ksym - XK_Left]);
 | 
			
		||||
			sprintf(buf, "\033%c%c", IS_SET(MODE_APPKEYPAD) ? 'O' : '[', (shift ? "dacb":"DACB")[ksym - XK_Left]);
 | 
			
		||||
			ttywrite(buf, 3);
 | 
			
		||||
			break;
 | 
			
		||||
		case XK_Insert:
 | 
			
		||||
| 
						 | 
				
			
			@ -1817,7 +1873,7 @@ resize(XEvent *e) {
 | 
			
		|||
	if(col == term.col && row == term.row)
 | 
			
		||||
		return;
 | 
			
		||||
	if(tresize(col, row))
 | 
			
		||||
		draw(SCREEN_REDRAW);
 | 
			
		||||
		draw();
 | 
			
		||||
	ttyresize(col, row);
 | 
			
		||||
	xresize(col, row);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1839,7 +1895,7 @@ run(void) {
 | 
			
		|||
		}
 | 
			
		||||
		if(FD_ISSET(cmdfd, &rfd)) {
 | 
			
		||||
			ttyread();
 | 
			
		||||
			draw(SCREEN_UPDATE); 
 | 
			
		||||
			draw(); 
 | 
			
		||||
		}
 | 
			
		||||
		while(XPending(xw.dpy)) {
 | 
			
		||||
			XNextEvent(xw.dpy, &ev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue