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