shithub: jot

Download patch

ref: b281eb93bd34cca00bfe31f0bb5fb4a7b4eb8c76
parent: e34b96039a780995c19333f56bdb7c13e72462e4
author: aap <aap@papnet.eu>
date: Sat Aug 31 05:01:14 EDT 2024

trying out notepad-style keyboard controls

--- a/inc.h
+++ b/inc.h
@@ -50,6 +50,7 @@
 	int nraw;
 
 	int posx;
+	int selflip;
 };
 
 void xinit(Text *x, Rectangle textr, Rectangle scrollr, Font *ft, Image *b, Image **cols);
@@ -102,3 +103,14 @@
 Timer *timerstart(int dt);
 void timerstop(Timer *t);
 void timercancel(Timer *t);
+
+
+
+
+typedef struct RKeyboardctl RKeyboardctl;
+struct RKeyboardctl
+{
+	Keyboardctl;
+	int kbdfd;
+};
+RKeyboardctl *initkbd(char *file, char *kbdfile);
--- /dev/null
+++ b/kbd.c
@@ -1,0 +1,107 @@
+#include "inc.h"
+
+static void
+_ioproc(void *arg)
+{
+	int m, n, nerr;
+	char buf[1024], *e, *p;
+	Rune r;
+	RKeyboardctl *kc;
+
+	kc = arg;
+	threadsetname("kbdproc");
+	n = 0;
+	nerr = 0;
+	if(kc->kbdfd >= 0){
+		while(kc->kbdfd >= 0){
+			m = read(kc->kbdfd, buf, sizeof(buf)-1);
+			if(m <= 0){
+				yield();	/* if error is due to exiting, we'll exit here */
+				if(kc->kbdfd < 0)
+					break;
+				fprint(2, "keyboard: short read: %r\n");
+				if(m<0 || ++nerr>10)
+					threadexits("read error");
+				continue;
+			}
+			/* one read can return multiple messages, delimited by NUL
+			 * split them up for sending on the channel */
+			e = buf+m;
+			e[-1] = 0;
+			e[0] = 0;
+			for(p = buf; p < e; p += strlen(p)+1)
+				chanprint(kc->c, "%s", p);
+		}
+	}else{
+		while(kc->consfd >= 0){
+			m = read(kc->consfd, buf+n, sizeof buf-n);
+			if(m <= 0){
+				yield();	/* if error is due to exiting, we'll exit here */
+				if(kc->consfd < 0)
+					break;
+				fprint(2, "keyboard: short read: %r\n");
+				if(m<0 || ++nerr>10)
+					threadexits("read error");
+				continue;
+			}
+			nerr = 0;
+			n += m;
+			while(n>0 && fullrune(buf, n)){
+				m = chartorune(&r, buf);
+				n -= m;
+				memmove(buf, buf+m, n);
+				if(chanprint(kc->c, "c%C", r) < 0)
+					break;
+			}
+		}
+	}
+	chanfree(kc->c);
+	free(kc->file);
+	free(kc);
+}
+
+RKeyboardctl*
+initkbd(char *file, char *kbdfile)
+{
+	RKeyboardctl *kc;
+	char *t;
+
+	if(file == nil)
+		file = "/dev/cons";
+	if(kbdfile == nil)
+		kbdfile = "/dev/kbd";
+
+	kc = mallocz(sizeof(RKeyboardctl), 1);
+	if(kc == nil)
+		return nil;
+	kc->file = strdup(file);
+// TODO: handle file == nil
+	kc->consfd = open(file, ORDWR|OCEXEC);
+	t = malloc(strlen(file)+16);
+	if(kc->consfd<0 || t==nil)
+		goto Error1;
+	sprint(t, "%sctl", file);
+	kc->ctlfd = open(t, OWRITE|OCEXEC);
+	if(kc->ctlfd < 0){
+		fprint(2, "initkeyboard: can't open %s: %r\n", t);
+		goto Error2;
+	}
+	if(ctlkeyboard(kc, "rawon") < 0){
+		fprint(2, "initkeyboard: can't turn on raw mode on %s: %r\n", t);
+		close(kc->ctlfd);
+		goto Error2;
+	}
+	free(t);
+	kc->kbdfd = open(kbdfile, OREAD|OCEXEC);
+	kc->c = chancreate(sizeof(char*), 20);
+	kc->pid = proccreate(_ioproc, kc, 4096);
+	return kc;
+
+Error2:
+	close(kc->consfd);
+Error1:
+	free(t);
+	free(kc->file);
+	free(kc);
+	return nil;
+}
--- a/main.c
+++ b/main.c
@@ -1,10 +1,11 @@
 #include "inc.h"
 #include <cursor.h>
 
-Keyboardctl *kbctl;
+RKeyboardctl *kbctl;
 Keyboardctl *fwdkc, kbdctl2;
 Mousectl *mctl;
 int shiftdown;	// TODO: needed?
+int ctldown;
 
 Text text;
 Image *colors[NCOL];
@@ -284,6 +285,95 @@
 }
 
 void
+xgetsel(Text *x, uint *q0, uint *q)
+{
+	if(x->selflip){
+		*q0 = x->q1;
+		*q = x->q0;
+	}else{
+		*q0 = x->q0;
+		*q = x->q1;
+	}
+}
+
+void
+xleftright(Text *x, int dir, int extend)
+{
+	uint q0, q;
+
+	xgetsel(x, &q0, &q);
+	if(dir < 0 && -dir > q)
+		q = 0;
+	else
+		q = min(q+dir, x->nr);
+	xsetselect(x, extend ? q0 : q, q);
+	xshow(x, q);
+}
+
+void
+xupdown(Text *x, int dir, int extend)
+{
+	Point p;
+	int py;
+	uint q0, q;
+
+	xgetsel(x, &q0, &q);
+
+	xshow(x, q);
+	p = frptofchar(x, q-x->org);
+	if(x->posx >= 0)
+		p.x = x->posx;
+	py = p.y;
+	p.y += dir*x->font->height;
+	if(p.y < x->Frame.r.min.y ||
+	   p.y > x->Frame.r.max.y-x->font->height){
+		xscrolln(x, dir);
+		p.y = py;
+	}
+	q = x->org+frcharofpt(x, p);
+
+	xsetselect(x, extend ? q0 : q, q);
+	xshow(x, q);
+	x->posx = p.x;
+}
+
+void
+xline(Text *x, int dir, int extend)
+{
+	uint q0, q;
+
+	xgetsel(x, &q0, &q);
+
+	if(dir < 0){
+		while(q > 0 && x->r[q-1] != '\n' &&
+		      q != x->qh)
+			q--;
+	}else{
+		while(q < x->nr && x->r[q] != '\n')
+			q++;
+	}
+
+	xsetselect(x, extend ? q0 : q, q);
+	xshow(x, q);
+}
+
+void
+xbegend(Text *x, int dir, int extend)
+{
+	uint q0, q;
+
+	xgetsel(x, &q0, &q);
+
+	if(dir < 0)
+		q = 0;
+	else
+		q = x->nr;
+
+	xsetselect(x, extend ? q0 : q, q);
+	xshow(x, q);
+}
+
+void
 keyctl(Text *x, Rune r)
 {
 	int nlines, n;
@@ -296,49 +386,54 @@
 		n = mousescrollsize(x->maxlines);
 		xscrolln(x, max(n, 1));
 		break;
-	case Kdown:
-		xscrolln(x, shiftdown ? 1 : nlines/3);
-		break;
 	case Kpgdown:
-		xscrolln(x, nlines*2/3);
+		if(ctldown)
+			xscrolln(x, nlines*2/3);
+		else
+			xscrolln(x, nlines*1/3);
 		break;
 	case Kscrolloneup:
 		n = mousescrollsize(x->maxlines);
 		xscrolln(x, -max(n, 1));
 		break;
-	case Kup:
-		xscrolln(x, -(shiftdown ? 1 : nlines/3));
-		break;
 	case Kpgup:
-		xscrolln(x, -nlines*2/3);
+		if(ctldown)
+			xscrolln(x, -nlines*2/3);
+		else
+			xscrolln(x, -nlines*1/3);
 		break;
 
-	case Khome:
-		xshow(x, 0);
+	/* Cursor movement */
+	case Kdown:
+		xupdown(x, 1, shiftdown);
 		break;
-	case Kend:
-		xshow(x, x->nr);
+	case Kup:
+		xupdown(x, -1, shiftdown);
 		break;
-
-	/* Cursor movement */
 	case Kleft:
-		if(x->q0 > 0)
-			xplacetick(x, x->q0-1);
+		xleftright(x, -1, shiftdown);
 		break;
 	case Kright:
-		if(x->q1 < x->nr)
-			xplacetick(x, x->q1+1);
+		xleftright(x, 1, shiftdown);
 		break;
+	case Khome:
+		if(ctldown)
+			xbegend(x, -1, shiftdown);
+		else
+			xline(x, -1, shiftdown);
+		break;
+	case Kend:
+		if(ctldown)
+			xbegend(x, 1, shiftdown);
+		else
+			xline(x, 1, shiftdown);
+		break;
+
 	case CTRL('A'):
-		while(x->q0 > 0 && x->r[x->q0-1] != '\n' &&
-		      x->q0 != x->qh)
-			x->q0--;
-		xplacetick(x, x->q0);
+		xline(x, -1, shiftdown);
 		break;
 	case CTRL('E'):
-		while(x->q0 < x->nr && x->r[x->q0] != '\n')
-			x->q0++;
-		xplacetick(x, x->q0);
+		xline(x, 1, shiftdown);
 		break;
 	case CTRL('B'):
 		xplacetick(x, x->qh);
@@ -379,17 +474,42 @@
 }
 
 void
+dumpkbd(char *s)
+{
+	Rune *rs;
+	int i;
+
+	rs = runesmprint("%s", s);
+	for(i = 0; rs[i]; i++)
+		print("%C %X\n", rs[i], rs[i]);
+	print("\n");
+	free(rs);
+}
+
+void
 kbthread(void*)
 {
+	char *s;
 	Rune r;
 
 	for(;;){
-		r = recvul(kbctl->c);
-		if(fwdkc)
-			send(fwdkc->c, &r);
-		else
-			keyctl(&text, r);
-		flushimage(display, 1);
+		s = recvp(kbctl->c);
+//dumpkbd(s);
+		if(*s == 'k' || *s == 'K'){
+			shiftdown = utfrune(s+1, Kshift) != nil;
+			ctldown = utfrune(s+1, Kctl) != nil;
+		}
+		if(*s == 'c'){
+			chartorune(&r, s+1);
+			if(r){
+				if(fwdkc)
+					send(fwdkc->c, &r);
+				else
+					keyctl(&text, r);
+				flushimage(display, 1);
+			}
+		}
+		free(s);
 	}
 }
 
@@ -414,9 +534,9 @@
 	if(initdraw(nil, nil, "jot") < 0)
 		sysfatal("initdraw: %r");
 
-	kbctl = initkeyboard("/dev/cons");
+	kbctl = initkbd(nil, nil);
 	if(kbctl == nil)
-		sysfatal("initkeyboard: %r");
+		sysfatal("inikeyboard: %r");
 	kbdctl2.c = chancreate(sizeof(Rune), 20);
 
 	mctl = initmouse("/dev/mouse", screen);
--- a/mkfile
+++ b/mkfile
@@ -4,6 +4,7 @@
 OFILES=\
 	main.$O \
 	text.$O \
+	kbd.$O \
 	time.$O
 
 HFILES=inc.h
--- a/text.c
+++ b/text.c
@@ -45,6 +45,49 @@
 	xscrdraw(x);
 }
 
+static void
+xuntick(Text *x)
+{
+	if(!x->ticked)
+		return;
+	if(x->p0 == x->p1)
+		frtick(x, frptofchar(x, x->p0), 0);
+	else
+		frtick(x, frptofchar(x, x->selflip ? x->p0 : x->p1), 0);
+}
+
+static void
+xtick(Text *x)
+{
+	if(x->p0 == x->p1 || x->selflip)
+		frtick(x, frptofchar(x, x->p0), 1);
+	else
+		frtick(x, frptofchar(x, x->p1), 1);
+}
+
+static void
+xfrdrawsel(Text *f, Point pt, ulong p0, ulong p1, int issel)
+{
+	Image *back, *text;
+
+	xuntick(f);
+
+	if(p0 == p1){
+		frtick(f, pt, issel);
+		return;
+	}
+
+	if(issel){
+		back = f->cols[HIGH];
+		text = f->cols[HTEXT];
+	}else{
+		back = f->cols[BACK];
+		text = f->cols[TEXT];
+	}
+
+	frdrawsel0(f, pt, p0, p1, back, text);
+}
+
 void
 xfullredraw(Text *x)
 {
@@ -51,10 +94,10 @@
 	xfill(x);
 	x->ticked = 0;
 	if(x->p0 > 0)
-		frdrawsel(x, frptofchar(x, 0), 0, x->p0, 0);
+		xfrdrawsel(x, frptofchar(x, 0), 0, x->p0, 0);
 	if(x->p1 < x->nchars)
-		frdrawsel(x, frptofchar(x, x->p1), x->p1, x->nchars, 0);
-	frdrawsel(x, frptofchar(x, x->p0), x->p0, x->p1, 1);
+		xfrdrawsel(x, frptofchar(x, x->p1), x->p1, x->nchars, 0);
+	xfrdrawsel(x, frptofchar(x, x->p0), x->p0, x->p1, 1);
 	x->lastsr = ZR;
 	xscrdraw(x);
 }
@@ -185,6 +228,14 @@
 {
 	int p0, p1;
 
+	xuntick(w);
+	w->selflip = q1 < q0;
+	if(w->selflip){
+		p0 = q0;
+		q0 = q1;
+		q1 = p0;
+	}
+
 	w->posx = -1;
 	/* w->p0 and w->p1 are always right; w->q0 and w->q1 may be off */
 	w->q0 = q0;
@@ -201,7 +252,7 @@
 	if(p1 > w->nchars)
 		p1 = w->nchars;
 	if(p0==w->p0 && p1==w->p1)
-		return;
+		goto Return;
 	/* screen disagrees with desired selection */
 	if(w->p1<=p0 || p1<=w->p0 || p0==p1 || w->p1==w->p0){
 		/* no overlap or too easy to bother trying */
@@ -228,6 +279,7 @@
     Return:
 	w->p0 = p0;
 	w->p1 = p1;
+	xtick(w);
 }
 
 static void
@@ -237,6 +289,7 @@
 	Rune *r;
 	uint n;
 
+	xuntick(w);
 	if(org>0 && !exact){
 		/* org is an estimate of the char posn; find a newline */
 		/* don't try harder than 256 chars */
@@ -262,9 +315,14 @@
 	w->org = org;
 	xfill(w);
 	xscrdraw(w);
+if(w->selflip)
+	xsetselect(w, w->q1, w->q0);
+else
 	xsetselect(w, w->q0, w->q1);
-	if(fixup && w->p1 > w->p0)
-		frdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
+	if(fixup && w->p1 > w->p0){
+		xfrdrawsel(w, frptofchar(w, w->p1-1), w->p1-1, w->p1, 1);
+		xtick(w);
+	}
 }
 
 
@@ -481,7 +539,7 @@
 		endq = x->org+x->p1;
 	}
 	xscrolln(x, dl);
-	xsetselect(x, min(selectq, endq), max(selectq, endq));
+	xsetselect(x, selectq, endq);
 }
 
 static void
@@ -499,7 +557,7 @@
 int
 iswordrune(Rune r)
 {
-	return isalpharune(r) || isdigitrune(r);
+	return r == '_' || isalpharune(r) || isdigitrune(r);
 }
 
 static int
@@ -666,8 +724,10 @@
 		selecttext = x;
 		selectmc = mc;
 		x->scroll = framescroll;
+		xuntick(x);
 		frselect(x, mc);
 		/* this is correct if the whole selection is visible */
+		x->selflip = x->p0 != x->p1 && x->p0 == frcharofpt(x, mc->xy);
 		q0 = x->org + x->p0;
 		q1 = x->org + x->p1;
 		/* otherwise replace one end with selectq */
@@ -675,7 +735,10 @@
 			q0 = selectq;
 		if(selectq > x->org+x->nchars)
 			q1 = selectq;
-		xsetselect(x, q0, q1);
+		if(x->selflip)
+			xsetselect(x, q1, q0);
+		else
+			xsetselect(x, q0, q1);
 
 		/* figure out whether it was a click */
 		if(q0 == q1 && mc->buttons == 0){